修复X-Forwarded-For ip地址伪造 安全问题

This commit is contained in:
weiweiw 2024-11-13 13:17:35 +08:00
parent 0cae52d5ee
commit 6a6d1d4c5e
11 changed files with 68 additions and 23 deletions

View File

@ -164,7 +164,7 @@ public class SysLogsVo {
try{ try{
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase(); String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
vo.setLogId(uuid); vo.setLogId(uuid);
String ip = IpUtils.getIpAddr(); String ip = loginUser.getIpaddr();
vo.setIp(ip); vo.setIp(ip);
// 设置方法名称 // 设置方法名称
String className = joinPoint.getTarget().getClass().getName(); String className = joinPoint.getTarget().getClass().getName();

View File

@ -238,7 +238,8 @@ public class PasswordValidatorService {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
try { try {
String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) { String ip = IpUtils.getIpAddr(systemConfig.getTrustedProxyIps());
if (IpUtils.isMatchedIp(blackStr,ip )) {
logAndThrowError(username, "访问IP已被列入系统黑名单", "访问IP已被列入系统黑名单"); logAndThrowError(username, "访问IP已被列入系统黑名单", "访问IP已被列入系统黑名单");
} }
} catch (Exception e) { } catch (Exception e) {
@ -269,7 +270,7 @@ public class PasswordValidatorService {
*/ */
public void handleIpValidation(String username, SysUser user) { public void handleIpValidation(String username, SysUser user) {
try { try {
String nowIp = IpUtils.getIpAddr(); String nowIp = IpUtils.getIpAddr(systemConfig.getTrustedProxyIps());
String hisIp = redisService.getCacheObject("IP:" + user.getUserId()); String hisIp = redisService.getCacheObject("IP:" + user.getUserId());
if (!nowIp.equals(hisIp)) { if (!nowIp.equals(hisIp)) {
recordLogService.saveErrorLogs(username, System.currentTimeMillis(), user.getUserId().toString(),"用户连续两次在不同IP登录"); recordLogService.saveErrorLogs(username, System.currentTimeMillis(), user.getUserId().toString(),"用户连续两次在不同IP登录");
@ -285,7 +286,7 @@ public class PasswordValidatorService {
List<Map<String, Object>> cacheList = redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST); List<Map<String, Object>> cacheList = redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST);
// 获取客户端的 IP 地址 // 获取客户端的 IP 地址
String ip = IpUtils.getIpAddr(); String ip = IpUtils.getIpAddr(systemConfig.getTrustedProxyIps());
// 遍历黑名单 // 遍历黑名单
for (Map<String, Object> map : cacheList) { for (Map<String, Object> map : cacheList) {

View File

@ -5,6 +5,7 @@ import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.global.SystemGlobal; import com.bonus.common.core.utils.global.SystemGlobal;
import com.bonus.common.log.enums.OperaResult; import com.bonus.common.log.enums.OperaResult;
import com.bonus.common.log.enums.OperaType; import com.bonus.common.log.enums.OperaType;
import com.bonus.config.SystemConfig;
import com.bonus.system.api.domain.SysLogsVo; import com.bonus.system.api.domain.SysLogsVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -15,6 +16,7 @@ import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.core.utils.ip.IpUtils; import com.bonus.common.core.utils.ip.IpUtils;
import com.bonus.system.api.RemoteLogService; import com.bonus.system.api.RemoteLogService;
import com.bonus.system.api.domain.SysLogininfor; import com.bonus.system.api.domain.SysLogininfor;
import org.springframework.util.ObjectUtils;
import java.util.UUID; import java.util.UUID;
@ -29,6 +31,8 @@ public class SysRecordLogService
{ {
@Autowired @Autowired
private RemoteLogService remoteLogService; private RemoteLogService remoteLogService;
@Autowired
private SystemConfig systemConfig;
/** /**
* 记录登录信息 * 记录登录信息
@ -42,7 +46,7 @@ public class SysRecordLogService
{ {
SysLogininfor logininfor = new SysLogininfor(); SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username); logininfor.setUserName(username);
logininfor.setIpaddr(IpUtils.getIpAddr()); logininfor.setIpaddr(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
logininfor.setMsg(message); logininfor.setMsg(message);
// 日志状态 // 日志状态
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
@ -70,7 +74,7 @@ public class SysRecordLogService
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase(); String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
sysLogsVo.setLogId(uuid); sysLogsVo.setLogId(uuid);
sysLogsVo.setOperaUserName(username); sysLogsVo.setOperaUserName(username);
sysLogsVo.setIp(IpUtils.getIpAddr()); sysLogsVo.setIp(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
sysLogsVo.setModel("系统认证模块"); sysLogsVo.setModel("系统认证模块");
sysLogsVo.setOperTime(DateUtils.getTime()); sysLogsVo.setOperTime(DateUtils.getTime());
sysLogsVo.setMethodType(SystemGlobal.POST); sysLogsVo.setMethodType(SystemGlobal.POST);
@ -119,6 +123,7 @@ public class SysRecordLogService
if (StringUtils.isNotEmpty(userId)){ if (StringUtils.isNotEmpty(userId)){
sysLogsVo.setUserId(userId); sysLogsVo.setUserId(userId);
} }
sysLogsVo.setIp(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
sysLogsVo.setResultData("用户登录成功"); sysLogsVo.setResultData("用户登录成功");
sysLogsVo.setTitle("系统登录"); sysLogsVo.setTitle("系统登录");
sysLogsVo.setModel("系统认证模块"); sysLogsVo.setModel("系统认证模块");
@ -127,7 +132,7 @@ public class SysRecordLogService
sysLogsVo.setMethod("login()"); sysLogsVo.setMethod("login()");
sysLogsVo.setLogId(uuid); sysLogsVo.setLogId(uuid);
sysLogsVo.setOperaUserName(username); sysLogsVo.setOperaUserName(username);
sysLogsVo.setIp(IpUtils.getIpAddr()); sysLogsVo.setIp(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
sysLogsVo.setParams("{\"username\":\""+username+"\"}"); sysLogsVo.setParams("{\"username\":\""+username+"\"}");
sysLogsVo.setOperateDetail("用户登录系统"); sysLogsVo.setOperateDetail("用户登录系统");
sysLogsVo.setErrType(errMessage); sysLogsVo.setErrType(errMessage);
@ -154,7 +159,7 @@ public class SysRecordLogService
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase(); String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
sysLogsVo.setLogId(uuid); sysLogsVo.setLogId(uuid);
sysLogsVo.setOperaUserName(username); sysLogsVo.setOperaUserName(username);
sysLogsVo.setIp(IpUtils.getIpAddr()); sysLogsVo.setIp(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
sysLogsVo.setModel("系统认证模块"); sysLogsVo.setModel("系统认证模块");
sysLogsVo.setLogType(0); sysLogsVo.setLogType(0);
if (StringUtils.isNotEmpty(userId)){ if (StringUtils.isNotEmpty(userId)){
@ -195,7 +200,7 @@ public class SysRecordLogService
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase(); String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
sysLogsVo.setLogId(uuid); sysLogsVo.setLogId(uuid);
sysLogsVo.setOperaUserName(username); sysLogsVo.setOperaUserName(username);
sysLogsVo.setIp(IpUtils.getIpAddr()); sysLogsVo.setIp(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
sysLogsVo.setModel("系统认证模块"); sysLogsVo.setModel("系统认证模块");
sysLogsVo.setLogType(0); sysLogsVo.setLogType(0);
if (StringUtils.isNotEmpty(userId)){ if (StringUtils.isNotEmpty(userId)){
@ -228,7 +233,7 @@ public class SysRecordLogService
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase(); String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
sysLogsVo.setLogId(uuid); sysLogsVo.setLogId(uuid);
sysLogsVo.setOperaUserName(username); sysLogsVo.setOperaUserName(username);
sysLogsVo.setIp(IpUtils.getIpAddr()); sysLogsVo.setIp(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
sysLogsVo.setModel("系统认证模块"); sysLogsVo.setModel("系统认证模块");
sysLogsVo.setLogType(0); sysLogsVo.setLogType(0);
if (StringUtils.isNotEmpty(userId)){ if (StringUtils.isNotEmpty(userId)){

View File

@ -50,6 +50,12 @@ public class SystemConfig {
*/ */
private String websocketurl; private String websocketurl;
/**
* 信任的代理ip list
*/
private List<String> trustedProxyIps;
@Data @Data
@RefreshScope @RefreshScope
public static class LoginConfig { public static class LoginConfig {

View File

@ -2,9 +2,11 @@ package com.bonus.common.core.utils.ip;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import com.bonus.common.core.utils.ServletUtils; import com.bonus.common.core.utils.ServletUtils;
import com.bonus.common.core.utils.StringUtils; import com.bonus.common.core.utils.StringUtils;
import org.springframework.util.ObjectUtils;
/** /**
* 获取IP方法 * 获取IP方法
@ -35,9 +37,9 @@ public class IpUtils
* *
* @return IP地址 * @return IP地址
*/ */
public static String getIpAddr() public static String getIpAddr(List<String> trustedProxy)
{ {
return getIpAddr(ServletUtils.getRequest()); return getIpAddr(ServletUtils.getRequest(), trustedProxy);
} }
/** /**
@ -46,7 +48,7 @@ public class IpUtils
* @param request 请求对象 * @param request 请求对象
* @return IP地址 * @return IP地址
*/ */
public static String getIpAddr(HttpServletRequest request) public static String getIpAddr(HttpServletRequest request,List<String> trustedProxy)
{ {
if (request == null) if (request == null)
{ {
@ -70,13 +72,22 @@ public class IpUtils
ip = request.getHeader("X-Real-IP"); ip = request.getHeader("X-Real-IP");
} }
if (ip == null || ip.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ip))
{ if (ip == null || ip.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ip)){
ip = request.getRemoteAddr(); ip = request.getRemoteAddr();
} }
String remoteAddr = request.getRemoteAddr();
if (!StringUtils.isEmpty(ip) && !StringUtils.isEmpty(remoteAddr) && !ObjectUtils.isEmpty(trustedProxy)) {
//使用代理的情况下确定代理是可信的
if (trustedProxy.contains(remoteAddr)) {
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
} }
}
if (!StringUtils.isEmpty(remoteAddr)) {
return "0:0:0:0:0:0:0:1".equals(remoteAddr) ? "127.0.0.1" : getMultistageReverseProxyIp(remoteAddr);
}
return IP_UNKNOWN;
}
/** /**
* 检查是否为内部IP地址 * 检查是否为内部IP地址

View File

@ -3,13 +3,16 @@ package com.bonus.common.log.aspect;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.bonus.common.core.utils.DateUtils; import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.SpringUtils;
import com.bonus.common.core.utils.global.SystemGlobal; import com.bonus.common.core.utils.global.SystemGlobal;
import com.bonus.common.log.annotation.SysLog; import com.bonus.common.log.annotation.SysLog;
import com.bonus.config.SystemConfig;
import com.bonus.system.api.domain.SysLogsVo; import com.bonus.system.api.domain.SysLogsVo;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
@ -23,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal; import org.springframework.core.NamedThreadLocal;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
@ -53,6 +57,9 @@ public class LogAspect
@Autowired @Autowired
private AsyncLogService asyncLogService; private AsyncLogService asyncLogService;
@Resource
private SystemConfig systemConfig;
/** /**
* 处理请求前执行 * 处理请求前执行
*/ */
@ -118,7 +125,7 @@ public class LogAspect
sysLogsVo.setOperateDetail(controllerLog.details()); sysLogsVo.setOperateDetail(controllerLog.details());
} }
sysLogsVo.setIp(IpUtils.getIpAddr()); sysLogsVo.setIp(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
// 设置方法名称 // 设置方法名称
String className = joinPoint.getTarget().getClass().getName(); String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName(); String methodName = joinPoint.getSignature().getName();

View File

@ -6,7 +6,9 @@ import cn.hutool.json.JSONObject;
import com.bonus.common.core.constant.SecurityConstants; import com.bonus.common.core.constant.SecurityConstants;
import com.bonus.common.core.domain.R; import com.bonus.common.core.domain.R;
import com.bonus.common.core.utils.DateUtils; import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.ip.IpUtils;
import com.bonus.common.security.utils.LogsUtils; import com.bonus.common.security.utils.LogsUtils;
import com.bonus.config.SystemConfig;
import com.bonus.system.api.RemoteLogService; import com.bonus.system.api.RemoteLogService;
import com.bonus.system.api.domain.SysLogsVo; import com.bonus.system.api.domain.SysLogsVo;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
@ -42,6 +44,8 @@ public class AuthLogic
public RemoteLogService logService = SpringUtils.getBean(RemoteLogService.class); public RemoteLogService logService = SpringUtils.getBean(RemoteLogService.class);
public SystemConfig systemConfig = SpringUtils.getBean(SystemConfig.class);
/** /**
* 会话注销 * 会话注销
*/ */
@ -180,6 +184,7 @@ public class AuthLogic
public void addErrorLogs(ProceedingJoinPoint joinPoint,RequiresPermissions requiresPermissions){ public void addErrorLogs(ProceedingJoinPoint joinPoint,RequiresPermissions requiresPermissions){
try{ try{
LoginUser loginUser = getLoginUser(); LoginUser loginUser = getLoginUser();
loginUser.setIpaddr(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
SysLogsVo vo=SysLogsVo.getExceedAuthorithSysLogsVo(loginUser,joinPoint); SysLogsVo vo=SysLogsVo.getExceedAuthorithSysLogsVo(loginUser,joinPoint);
LogsUtils.setRequestValue(joinPoint,vo,null); LogsUtils.setRequestValue(joinPoint,vo,null);
SysLogsVo sysLogsVo=new SysLogsVo(); SysLogsVo sysLogsVo=new SysLogsVo();

View File

@ -2,6 +2,9 @@ package com.bonus.common.security.feign;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import com.bonus.common.core.utils.SpringUtils;
import com.bonus.config.SystemConfig;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.bonus.common.core.constant.SecurityConstants; import com.bonus.common.core.constant.SecurityConstants;
import com.bonus.common.core.utils.ServletUtils; import com.bonus.common.core.utils.ServletUtils;
@ -18,6 +21,8 @@ import feign.RequestTemplate;
@Component @Component
public class FeignRequestInterceptor implements RequestInterceptor public class FeignRequestInterceptor implements RequestInterceptor
{ {
public SystemConfig systemConfig = SpringUtils.getBean(SystemConfig.class);
@Override @Override
public void apply(RequestTemplate requestTemplate) public void apply(RequestTemplate requestTemplate)
{ {
@ -48,7 +53,7 @@ public class FeignRequestInterceptor implements RequestInterceptor
} }
// 配置客户端IP // 配置客户端IP
requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr()); requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
} }
} }
} }

View File

@ -20,6 +20,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -71,7 +72,7 @@ public class TokenService {
loginUser.setToken(token); loginUser.setToken(token);
loginUser.setUserid(userId); loginUser.setUserid(userId);
loginUser.setUsername(userName); loginUser.setUsername(userName);
loginUser.setIpaddr(IpUtils.getIpAddr()); loginUser.setIpaddr(IpUtils.getIpAddr(systemConfig.getTrustedProxyIps()));
refreshToken(loginUser); refreshToken(loginUser);
// Jwt存储信息 // Jwt存储信息
Map<String, Object> claimsMap = new HashMap<String, Object>(16); Map<String, Object> claimsMap = new HashMap<String, Object>(16);

View File

@ -76,7 +76,7 @@ public class SysUserController extends BaseController {
@RequiresPermissionsOrInnerAuth(innerAuth = @InnerAuth, requiresPermissions = @RequiresPermissions("system:user:list")) @RequiresPermissionsOrInnerAuth(innerAuth = @InnerAuth, requiresPermissions = @RequiresPermissions("system:user:list"))
@GetMapping("/list") @GetMapping("/list")
@PreventRepeatSubmit @PreventRepeatSubmit
// @SysLog(title = "用户管理", businessType = OperaType.QUERY, logType = 0, module = "系统管理->用户管理", details = "查询用户列表") @SysLog(title = "用户管理", businessType = OperaType.QUERY, logType = 0, module = "系统管理->用户管理", details = "查询用户列表")
public TableDataInfo list(SysUser user) { public TableDataInfo list(SysUser user) {
try { try {
startPage(); startPage();

View File

@ -1,5 +1,6 @@
package com.bonus.system.service.impl; package com.bonus.system.service.impl;
import com.bonus.config.SystemConfig;
import com.bonus.system.warning.SysWarning; import com.bonus.system.warning.SysWarning;
import com.bonus.system.warning.WaringLogEvent; import com.bonus.system.warning.WaringLogEvent;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -25,6 +26,7 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -47,6 +49,8 @@ public class SysLogServiceImpl implements ISysLogService {
@Autowired @Autowired
private ApplicationEventPublisher eventPublisher; private ApplicationEventPublisher eventPublisher;
@Autowired
private SystemConfig systemConfig;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -82,9 +86,9 @@ public class SysLogServiceImpl implements ISysLogService {
public void saveLogs(SysLogsVo sysLog, HttpServletRequest request) { public void saveLogs(SysLogsVo sysLog, HttpServletRequest request) {
try{ try{
String loginUuid = IdUtils.fastUUID(); String loginUuid = IdUtils.fastUUID();
String ip = IpUtils.getIpAddr(request); String ip = IpUtils.getIpAddr(request, systemConfig.getTrustedProxyIps());
sysLog.setLogId(loginUuid);
sysLog.setIp(ip); sysLog.setIp(ip);
sysLog.setLogId(loginUuid);
sysLog.setGrade(""); sysLog.setGrade("");
sysLog.setErrType("越权访问"); sysLog.setErrType("越权访问");
sysLog.setFailureReason("页面未授权"); sysLog.setFailureReason("页面未授权");