diff --git a/bonus-auth/src/main/java/com/bonus/auth/service/PasswordValidatorService.java b/bonus-auth/src/main/java/com/bonus/auth/service/PasswordValidatorService.java index a4da978..0a793e2 100644 --- a/bonus-auth/src/main/java/com/bonus/auth/service/PasswordValidatorService.java +++ b/bonus-auth/src/main/java/com/bonus/auth/service/PasswordValidatorService.java @@ -1,6 +1,7 @@ package com.bonus.auth.service; import com.bonus.common.core.constant.CacheConstants; +import com.bonus.common.core.constant.SecurityConstants; import com.bonus.common.core.constant.UserConstants; import com.bonus.common.core.domain.R; import com.bonus.common.core.enums.UserStatus; @@ -12,6 +13,7 @@ import com.bonus.common.core.utils.ip.IpUtils; import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.redis.service.RedisService; import com.bonus.config.SystemConfig; +import com.bonus.system.api.RemoteUserService; import com.bonus.system.api.domain.SysUser; import com.bonus.system.api.model.LoginUser; import org.springframework.stereotype.Component; @@ -31,6 +33,9 @@ public class PasswordValidatorService { @Resource private SysRecordLogService recordLogService; + @Resource + private RemoteUserService remoteUserService; + /** * 对新密码进行校验,返回布尔值表示密码是否符合要求。 * @@ -259,8 +264,11 @@ public class PasswordValidatorService { public void handleIpValidation(String username, SysUser user) { try { String nowIp = IpUtils.getIpAddr(); - String hisIp = redisService.getCacheObject("IP:" + user.getUserId()); + AjaxResult ajaxResult = remoteUserService.selectSysIpWhitelist(SecurityConstants.INNER); + if (ajaxResult.isSuccess()){ + } + String hisIp = redisService.getCacheObject("IP:" + user.getUserId()); if (!nowIp.equals(hisIp)) { recordLogService.saveErrorLogs(username, System.currentTimeMillis(), user.getUserId().toString()); } diff --git a/bonus-auth/src/main/java/com/bonus/auth/service/SysPasswordService.java b/bonus-auth/src/main/java/com/bonus/auth/service/SysPasswordService.java index 77c94b6..8123e92 100644 --- a/bonus-auth/src/main/java/com/bonus/auth/service/SysPasswordService.java +++ b/bonus-auth/src/main/java/com/bonus/auth/service/SysPasswordService.java @@ -2,18 +2,23 @@ package com.bonus.auth.service; import com.bonus.common.core.constant.CacheConstants; import com.bonus.common.core.constant.Constants; +import com.bonus.common.core.constant.SecurityConstants; import com.bonus.common.core.domain.R; import com.bonus.common.core.exception.CaptchaException; import com.bonus.common.core.exception.ServiceException; import com.bonus.common.core.utils.StringUtils; import com.bonus.common.core.utils.VerificationCodeUtils; import com.bonus.common.core.utils.sms.SmsUtils; +import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.redis.service.RedisService; import com.bonus.common.security.utils.SecurityUtils; +import com.bonus.system.api.RemoteConfigService; import com.bonus.system.api.domain.SysUser; +import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import javax.annotation.Resource; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -34,6 +39,9 @@ public class SysPasswordService { @Autowired private SysRecordLogService recordLogService; + @Resource + private RemoteConfigService configService; + /** * 登录账户密码错误次数缓存键名 * @@ -46,26 +54,31 @@ public class SysPasswordService { public void validate(SysUser user, String password, long startTime) { String username = user.getUserName(); - int retryCount = redisService.getCacheObject(getCacheKey(username)); - - // 基本锁定时间为 lockTime - long dynamicLockTime = lockTime; - - if (retryCount > 0 && retryCount % 5 == 0) { - dynamicLockTime += (retryCount / 5) * lockTime; // 每5次增加一个基本锁定时间 + Integer retryCount = redisService.getCacheObject(getCacheKey(username)); + Integer times = 5; + Integer lockTime = 20; + AjaxResult timesAjaxResult = configService.getConfigKey("sys.login.failed.times", SecurityConstants.INNER); + AjaxResult lockTimeAjaxResult = configService.getConfigKey("sys.login.failed.locktime", SecurityConstants.INNER); + if (timesAjaxResult.isSuccess()){ + times = Integer.parseInt(timesAjaxResult.get("msg").toString()); + } + if (lockTimeAjaxResult.isSuccess()){ + lockTime = Integer.parseInt(lockTimeAjaxResult.get("msg").toString()); } - if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) { + if (ObjectUtils.isEmpty(retryCount)){ + retryCount = 0; + } + if (retryCount >= times) { long time = redisService.getExpire(getCacheKey(username)); - long times = time / 60 + 1; - String errMsg = String.format("密码输入错误%s次,帐户锁定,请%s分钟后重试", maxRetryCount, times); + String errMsg = String.format("密码输入错误%s次,帐户锁定,请%s分钟后重试", maxRetryCount, time / 60 + 1); recordLogService.saveLogs(username, startTime, "用户账号锁定", "用户账号已锁定,请" + times + "后重试", null, null); throw new ServiceException(errMsg); } if (!matches(user, password)) { retryCount = retryCount + 1; recordLogService.saveLogs(username, startTime, "密码输入错误", "用户不存在/密码错误", null, null); - redisService.setCacheObject(getCacheKey(username), retryCount, dynamicLockTime, TimeUnit.MINUTES); + redisService.setCacheObject(getCacheKey(username), retryCount, (long)lockTime, TimeUnit.MINUTES); throw new ServiceException("用户不存在/密码错误"); } else { clearLoginRecordCache(username); diff --git a/bonus-auth/src/main/java/com/bonus/auth/service/UsernamePasswordLoginStrategy.java b/bonus-auth/src/main/java/com/bonus/auth/service/UsernamePasswordLoginStrategy.java index 852468f..2c81b4a 100644 --- a/bonus-auth/src/main/java/com/bonus/auth/service/UsernamePasswordLoginStrategy.java +++ b/bonus-auth/src/main/java/com/bonus/auth/service/UsernamePasswordLoginStrategy.java @@ -39,11 +39,10 @@ public class UsernamePasswordLoginStrategy implements LoginStrategy { passwordValidatorService.validateIpBlacklist(username); //通过用户名获取人员信息 R userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); + passwordValidatorService.validateUserResult(username, userResult); //获取用户信息 LoginUser userInfo = userResult.getData(); SysUser user = userInfo.getSysUser(); - // 验证用户查询结果 - passwordValidatorService.validateUserResult(username, userResult); passwordValidatorService.validateApprovalStatus(username, user); // 验证用户状态 passwordValidatorService.validateUserStatus(username, user); diff --git a/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/constant/CacheConstants.java b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/constant/CacheConstants.java index 28139e9..65d39eb 100644 --- a/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/constant/CacheConstants.java +++ b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/constant/CacheConstants.java @@ -66,5 +66,5 @@ public class CacheConstants { /** * 登录IP黑名单 cache key */ - public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList"; + public static final String SYS_LOGIN_BLACKIPLIST ="blackIPList"; } diff --git a/bonus-common/bonus-common-redis/src/main/java/com/bonus/common/redis/service/RedisService.java b/bonus-common/bonus-common-redis/src/main/java/com/bonus/common/redis/service/RedisService.java index a9e7e1f..392b3b9 100644 --- a/bonus-common/bonus-common-redis/src/main/java/com/bonus/common/redis/service/RedisService.java +++ b/bonus-common/bonus-common-redis/src/main/java/com/bonus/common/redis/service/RedisService.java @@ -1,18 +1,12 @@ package com.bonus.common.redis.service; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.BoundSetOperations; -import org.springframework.data.redis.core.HashOperations; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.ValueOperations; +import org.springframework.data.redis.core.*; import org.springframework.stereotype.Component; +import java.util.*; +import java.util.concurrent.TimeUnit; + /** * spring redis 工具类 * @@ -24,7 +18,8 @@ public class RedisService { @Autowired public RedisTemplate redisTemplate; - + @Autowired + private StringRedisTemplate stringRedisTemplate; /** * 缓存基本的对象,Integer、String、实体类等 * @@ -273,4 +268,16 @@ public class RedisService { return redisTemplate.opsForValue().setIfAbsent(key,value,lt,tu); } + + + /** + * 根据固定前缀获取 Redis 中符合条件的哈希字段 + * + * @param prefix 需要匹配的前缀 + * @return 符合前缀的哈希字段值列表 + */ + public Set getKeysByPrefix(String prefix) { + return stringRedisTemplate.keys(prefix + "*"); + } + } diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/filter/IpFilter.java b/bonus-gateway/src/main/java/com/bonus/gateway/filter/IpFilter.java new file mode 100644 index 0000000..21a3956 --- /dev/null +++ b/bonus-gateway/src/main/java/com/bonus/gateway/filter/IpFilter.java @@ -0,0 +1,154 @@ +package com.bonus.gateway.filter; + +import com.bonus.common.core.constant.CacheConstants; +import com.bonus.common.redis.service.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + +/** + * @author bonus + */ +@Component +@Slf4j +public class IpFilter implements GlobalFilter, Ordered { + @Resource + private RedisService redisService; + /** + * Process the Web request and (optionally) delegate to the next {@code GatewayFilter} + * through the given {@link GatewayFilterChain}. + * + * @param exchange the current server exchange + * @param chain provides a way to delegate to the next filter + * @return {@code Mono} to indicate when request processing is complete + */ + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + List> cacheList = redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST); + + // 获取客户端的 IP 地址 + String ip = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For"); + for (Map map : cacheList) { + String ipAddress = map.containsKey("ipAddress") ? map.get("ipAddress").toString() : null; + String ipRangeEnd = map.containsKey("ipRangeEnd") ?map.get("ipRangeEnd").toString(): null; + String ipRangeStart = map.containsKey("ipRangeStart")?map.get("ipRangeStart").toString():null; + String accessStartTime =map.containsKey("accessStartTime")? map.get("accessStartTime").toString():null; + String accessEndTime = map.containsKey("accessEndTime")?map.get("accessEndTime").toString():null; + if (ObjectUtils.isEmpty(ipAddress)){ + if (isIpInRange(ip, ipRangeStart, ipRangeEnd)){ + if (ObjectUtils.isNotEmpty(accessStartTime)){ + boolean currentTimeInRange = isCurrentTimeInRange(accessStartTime, accessEndTime); + if (!currentTimeInRange){ + // 完成响应 + exchange.getResponse().setStatusCode(org.springframework.http.HttpStatus.FORBIDDEN); + return exchange.getResponse().setComplete(); + }else { + return chain.filter(exchange); + } + }else { + return chain.filter(exchange); + } + + } + }else { + if (ipAddress.equals(ip)){ + if (ObjectUtils.isNotEmpty(accessStartTime)){ + boolean currentTimeInRange = isCurrentTimeInRange(accessStartTime, accessEndTime); + if (!currentTimeInRange){ + // 完成响应 + exchange.getResponse().setStatusCode(org.springframework.http.HttpStatus.FORBIDDEN); + return exchange.getResponse().setComplete(); + }else { + return chain.filter(exchange); + } + }else { + return chain.filter(exchange); + } + } + } + } + exchange.getResponse().setStatusCode(org.springframework.http.HttpStatus.FORBIDDEN); + return exchange.getResponse().setComplete(); + } + /** + * 检查给定的IP地址是否在指定的网段区间内 + * + * @param ip 要检查的IP地址,例如 "192.168.1.10" + * @param startIp 区间开始的IP地址,例如 "192.168.1.0" + * @param endIp 区间结束的IP地址,例如 "192.168.1.255" + * @return true 如果IP在区间内;否则返回 false + */ + public static boolean isIpInRange(String ip, String startIp, String endIp) { + try { + // 将 IP 地址、起始 IP 和结束 IP 转换为整数 + long ipToCheck = ipToLong(InetAddress.getByName(ip)); + long start = ipToLong(InetAddress.getByName(startIp)); + long end = ipToLong(InetAddress.getByName(endIp)); + + // 检查 IP 是否在区间内 + return ipToCheck >= start && ipToCheck <= end; + } catch (UnknownHostException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将IP地址转换为整数 + * + * @param inetAddress IP地址对象 + * @return 转换后的长整数 + */ + private static long ipToLong(InetAddress inetAddress) { + byte[] octets = inetAddress.getAddress(); + long result = 0; + for (byte octet : octets) { + result = (result << 8) | (octet & 0xFF); + } + return result; + } + public static boolean isCurrentTimeInRange(String start, String end) { + // 定义时间格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss"); + + // 将字符串转换为 LocalTime + LocalTime startTime = LocalTime.parse(start, formatter); + LocalTime endTime = LocalTime.parse(end, formatter); + + // 获取当前时间 + LocalTime currentTime = LocalTime.now(); + + // 检查当前时间是否在指定的时间范围内 + return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime); + } + + /** + * Get the order value of this object. + *

Higher values are interpreted as lower priority. As a consequence, + * the object with the lowest value has the highest priority (somewhat + * analogous to Servlet {@code load-on-startup} values). + *

Same order values will result in arbitrary sort positions for the + * affected objects. + * + * @return the order value + * @see #HIGHEST_PRECEDENCE + * @see #LOWEST_PRECEDENCE + */ + @Override + public int getOrder() { + return 0; + } +} diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/filter/RequestCoverFilter.java b/bonus-gateway/src/main/java/com/bonus/gateway/filter/RequestCoverFilter.java index 1b80906..4b78678 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/filter/RequestCoverFilter.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/filter/RequestCoverFilter.java @@ -148,7 +148,7 @@ public class RequestCoverFilter implements GlobalFilter, Ordered { return exchange.getResponse().setComplete(); } // 解密请求体 - if ( encrypt) { + if (encrypt) { try { requestBody = Sm4Utils.decrypt(requestBody); } catch (Exception e) { @@ -160,11 +160,11 @@ public class RequestCoverFilter implements GlobalFilter, Ordered { if (ObjectUtils.isEmpty(requestBody)) { return exchange.getResponse().setComplete(); } - - String providedHmac = requestBody.split("\\|")[1]; requestBody = requestBody.split("\\|")[0]; // 校验数据完整性 if (integrality) { + String providedHmac = requestBody.split("\\|")[1]; + integrityVerification(providedHmac, requestBody); } diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/handler/GatewayExceptionHandler.java b/bonus-gateway/src/main/java/com/bonus/gateway/handler/GatewayExceptionHandler.java index 13280ab..14da3a0 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/handler/GatewayExceptionHandler.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/handler/GatewayExceptionHandler.java @@ -49,7 +49,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler { msg = "内部服务器错误"; } - System.err.println(ex.getMessage()); + ex.printStackTrace(); log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); return ServletUtils.webFluxResponseWriter(response, msg); diff --git a/bonus-modules/bonus-job/src/main/java/com/bonus/job/mapper/SysJobMapper.java b/bonus-modules/bonus-job/src/main/java/com/bonus/job/mapper/SysJobMapper.java index bce7521..f8d7fcb 100644 --- a/bonus-modules/bonus-job/src/main/java/com/bonus/job/mapper/SysJobMapper.java +++ b/bonus-modules/bonus-job/src/main/java/com/bonus/job/mapper/SysJobMapper.java @@ -67,7 +67,7 @@ public interface SysJobMapper */ public int insertJob(SysJob job); - List selectUserList(); + List selectUserList(SysUser user); /** * 修改用户状态 diff --git a/bonus-modules/bonus-job/src/main/java/com/bonus/job/task/SysTask.java b/bonus-modules/bonus-job/src/main/java/com/bonus/job/task/SysTask.java index 9d691ef..b30140d 100644 --- a/bonus-modules/bonus-job/src/main/java/com/bonus/job/task/SysTask.java +++ b/bonus-modules/bonus-job/src/main/java/com/bonus/job/task/SysTask.java @@ -28,10 +28,11 @@ public class SysTask @Resource private SysJobMapper mapper; - public void checkUserLastLoginTime(){ try{ - List sysUsers = mapper.selectUserList(); + SysUser user = new SysUser(); + user.setStatus("0"); + List sysUsers = mapper.selectUserList(user); sysUsers.forEach(item -> { Date loginDate = item.getLoginDate(); if (ObjectUtils.isEmpty(item.getLoginDate())){ @@ -52,4 +53,29 @@ public class SysTask logger.error("修改用户状态异常,{}",e.getMessage()); } } + + public void checkUserPermanent(){ + try{ + SysUser user = new SysUser(); + user.setIsPermanent("0"); + List sysUsers = mapper.selectUserList(user); + sysUsers.forEach(item -> { + long minutes = DateUtils.minutesBetween(item.getCreateTime(), DateUtils.getNowDate()); + if (minutes >= LAST_LOGIN_TIME_INTERVAL*3){ + int i = mapper.updateUser(item.getUserId()); + if (i>0){ + logger.error("修改用户状态,用户id为:{},用户名为:{}",item.getUserId(),item.getUserName()); + }else { + logger.error("修改用户状态失败,用户id为:{},用户名为:{}",item.getUserId(),item.getUserName()); + } + } + }); + } + catch (Exception e){ + logger.error("修改用户状态异常,{}",e.getMessage()); + } + } + + + } diff --git a/bonus-modules/bonus-job/src/main/resources/mapper/job/SysJobMapper.xml b/bonus-modules/bonus-job/src/main/resources/mapper/job/SysJobMapper.xml index 82574de..8c13103 100644 --- a/bonus-modules/bonus-job/src/main/resources/mapper/job/SysJobMapper.xml +++ b/bonus-modules/bonus-job/src/main/resources/mapper/job/SysJobMapper.xml @@ -51,7 +51,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where job_id = #{jobId} - + delete from sys_job where job_id = #{jobId} @@ -112,7 +112,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"