mirror of https://gitee.com/stylefeng/roses
【7.2.2】更新登录重试错误次数限制机制
parent
fc783bbdf6
commit
6995c44b69
|
@ -9,17 +9,18 @@ package cn.stylefeng.roses.kernel.auth.api.constants;
|
|||
public interface LoginCacheConstants {
|
||||
|
||||
/**
|
||||
* 登录最大次数
|
||||
* 登录失败最大次数
|
||||
*/
|
||||
Integer MAX_LOGIN_COUNT = 5;
|
||||
Integer MAX_ERROR_LOGIN_COUNT = 5;
|
||||
|
||||
/**
|
||||
* 登录冻结缓存前缀
|
||||
*/
|
||||
String LOGIN_CACHE_PREFIX = "login:";
|
||||
String LOGIN_ERROR_CACHE_PREFIX = "LOGIN:COUNT:";
|
||||
|
||||
/**
|
||||
* 冻结时间
|
||||
* 冻结时间:30分钟
|
||||
*/
|
||||
Long LOGIN_CACHE_TIMEOUT_SECONDS = 1800L;
|
||||
|
||||
}
|
||||
|
|
|
@ -124,9 +124,9 @@ public enum AuthExceptionEnum implements AbstractExceptionEnum {
|
|||
CANT_REQUEST_UN_OPEN_API(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "17", "无法访问未经授权的接口,未授权url为:{}"),
|
||||
|
||||
/**
|
||||
* 超过最大登录次数
|
||||
* 密码重试次数过多,帐号被锁定
|
||||
*/
|
||||
EXCEED_MAX_LOGIN_COUNT(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "18", "超过最大登录次数,帐号被锁定");
|
||||
LOGIN_LOCKED(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "18", "密码重试次数过多,帐号被锁定");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
|
|
|
@ -126,11 +126,6 @@
|
|||
<version>${roses.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>system-business-user</artifactId>
|
||||
<version>${roses.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -41,7 +41,6 @@ import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
|
|||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.auth.api.expander.AuthConfigExpander;
|
||||
import cn.stylefeng.roses.kernel.auth.api.expander.LoginConfigExpander;
|
||||
import cn.stylefeng.roses.kernel.auth.api.password.PasswordStoredEncryptApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.password.PasswordTransferEncryptApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.SsoProperties;
|
||||
|
@ -69,8 +68,6 @@ import cn.stylefeng.roses.kernel.system.api.ResourceServiceApi;
|
|||
import cn.stylefeng.roses.kernel.system.api.UserServiceApi;
|
||||
import cn.stylefeng.roses.kernel.system.api.enums.UserStatusEnum;
|
||||
import cn.stylefeng.roses.kernel.system.api.pojo.user.UserLoginInfoDTO;
|
||||
import cn.stylefeng.roses.kernel.system.modular.user.entity.SysUser;
|
||||
import cn.stylefeng.roses.kernel.system.modular.user.service.SysUserService;
|
||||
import cn.stylefeng.roses.kernel.validator.api.exception.enums.ValidatorExceptionEnum;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
@ -124,11 +121,8 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
@Resource
|
||||
private ResourceServiceApi resourceServiceApi;
|
||||
|
||||
@Resource
|
||||
private SysUserService sysUserService;
|
||||
|
||||
@Resource
|
||||
private CacheOperatorApi<String> loginCacheOperatorApi;
|
||||
@Resource(name = "loginErrorCountCacheApi")
|
||||
private CacheOperatorApi<Integer> loginErrorCountCacheApi;
|
||||
|
||||
@Override
|
||||
public LoginResponse login(LoginRequest loginRequest) {
|
||||
|
@ -265,26 +259,6 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
* @date 2020/10/21 16:59
|
||||
*/
|
||||
private LoginResponse loginAction(LoginRequest loginRequest, Boolean validatePassword, String caToken) {
|
||||
SysUser userByAccount = sysUserService.getUserByAccount(loginRequest.getAccount());
|
||||
// 判断登录错误检测是否开启
|
||||
if (LoginConfigExpander.getAccountErrorDetectionFlag()) {
|
||||
// 判断错误次数,超过最大放入缓存中
|
||||
if (StrUtil.isBlank(loginCacheOperatorApi.get(userByAccount.getUserId().toString()))) {
|
||||
if (userByAccount.getLoginCount() > LoginCacheConstants.MAX_LOGIN_COUNT) {
|
||||
loginCacheOperatorApi.put(userByAccount.getUserId().toString(), "true", 1800L);
|
||||
throw new AuthException(AuthExceptionEnum.EXCEED_MAX_LOGIN_COUNT);
|
||||
}
|
||||
} else {
|
||||
throw new AuthException(AuthExceptionEnum.EXCEED_MAX_LOGIN_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 获取用户密码的加密值和用户的状态
|
||||
UserLoginInfoDTO userValidateInfo = userServiceApi.getUserLoginInfo(loginRequest.getAccount());
|
||||
|
||||
// 8. 获取LoginUser,用于用户的缓存
|
||||
LoginUser loginUser = userValidateInfo.getLoginUser();
|
||||
|
||||
// 1.参数为空校验
|
||||
if (validatePassword) {
|
||||
if (loginRequest == null || StrUtil.hasBlank(loginRequest.getAccount(), loginRequest.getPassword())) {
|
||||
|
@ -296,6 +270,12 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
}
|
||||
}
|
||||
|
||||
// 1.2 判断账号是否密码重试次数过多被冻结
|
||||
Integer loginErrorCount = loginErrorCountCacheApi.get(loginRequest.getAccount());
|
||||
if (loginErrorCount != null && loginErrorCount >= LoginCacheConstants.MAX_ERROR_LOGIN_COUNT) {
|
||||
throw new AuthException(AuthExceptionEnum.LOGIN_LOCKED);
|
||||
}
|
||||
|
||||
// 2. 如果开启了验证码校验,则验证当前请求的验证码是否正确
|
||||
if (SecurityConfigExpander.getCaptchaOpen()) {
|
||||
String verKey = loginRequest.getVerKey();
|
||||
|
@ -305,8 +285,6 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_EMPTY);
|
||||
}
|
||||
if (!captchaApi.validateCaptcha(verKey, verCode)) {
|
||||
// 登录失败日志
|
||||
loginLogServiceApi.loginFail(loginUser.getUserId(), "验证码错误");
|
||||
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_ERROR);
|
||||
}
|
||||
}
|
||||
|
@ -320,8 +298,6 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_EMPTY);
|
||||
}
|
||||
if (!dragCaptchaApi.validateCaptcha(verKey, Convert.toInt(verXLocationValue))) {
|
||||
// 登录失败日志
|
||||
loginLogServiceApi.loginFail(loginUser.getUserId(), "拖拽验证码错误");
|
||||
throw new AuthException(ValidatorExceptionEnum.DRAG_CAPTCHA_ERROR);
|
||||
}
|
||||
}
|
||||
|
@ -342,15 +318,17 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
return new LoginResponse(remoteLoginCode);
|
||||
}
|
||||
|
||||
// 5. 获取用户密码的加密值和用户的状态
|
||||
UserLoginInfoDTO userValidateInfo = userServiceApi.getUserLoginInfo(loginRequest.getAccount());
|
||||
|
||||
// 6. 校验用户密码是否正确
|
||||
if (validatePassword) {
|
||||
Boolean checkResult = passwordStoredEncryptApi.checkPassword(loginRequest.getPassword(), userValidateInfo.getUserPasswordHexed());
|
||||
if (!checkResult) {
|
||||
//更新登录次数
|
||||
userByAccount.setLoginCount(userByAccount.getLoginCount() + 1);
|
||||
sysUserService.updateById(userByAccount);
|
||||
// 登录失败日志
|
||||
loginLogServiceApi.loginFail(loginUser.getUserId(), "帐号或密码错误");
|
||||
if (loginErrorCount == null) {
|
||||
loginErrorCount = 0;
|
||||
}
|
||||
loginErrorCountCacheApi.put(loginRequest.getAccount(), loginErrorCount + 1);
|
||||
throw new AuthException(AuthExceptionEnum.USERNAME_PASSWORD_ERROR);
|
||||
}
|
||||
}
|
||||
|
@ -360,6 +338,9 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
throw new AuthException(AuthExceptionEnum.USER_STATUS_ERROR, UserStatusEnum.getCodeMessage(userValidateInfo.getUserStatus()));
|
||||
}
|
||||
|
||||
// 8. 获取LoginUser,用于用户的缓存
|
||||
LoginUser loginUser = userValidateInfo.getLoginUser();
|
||||
|
||||
// 9. 生成用户的token
|
||||
DefaultJwtPayload defaultJwtPayload = new DefaultJwtPayload(loginUser.getUserId(), loginUser.getAccount(), loginRequest.getRememberMe(), caToken);
|
||||
String jwtToken = JwtContext.me().generateTokenDefaultPayload(defaultJwtPayload);
|
||||
|
@ -388,10 +369,6 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
String ip = HttpServletUtil.getRequestClientIp(HttpServletUtil.getRequest());
|
||||
userServiceApi.updateUserLoginInfo(loginUser.getUserId(), new Date(), ip);
|
||||
|
||||
//重置登录次数
|
||||
userByAccount.setLoginCount(1);
|
||||
sysUserService.updateById(userByAccount);
|
||||
|
||||
// 13.登录成功日志
|
||||
loginLogServiceApi.loginSuccess(loginUser.getUserId());
|
||||
}
|
||||
|
@ -442,11 +419,6 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
|
||||
@Override
|
||||
public void cancelFreeze(LoginRequest loginRequest) {
|
||||
SysUser sysUser = sysUserService.getUserByAccount(loginRequest.getAccount());
|
||||
sysUser.setLoginCount(1);
|
||||
// 修改数据库中的登录次数
|
||||
sysUserService.updateById(sysUser);
|
||||
// 删除缓存中的数据
|
||||
loginCacheOperatorApi.remove(sysUser.getUserId().toString());
|
||||
loginErrorCountCacheApi.remove(loginRequest.getAccount());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package cn.stylefeng.roses.kernel.auth.cache;
|
||||
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import cn.stylefeng.roses.kernel.auth.api.constants.LoginCacheConstants;
|
||||
import cn.stylefeng.roses.kernel.cache.memory.AbstractMemoryCacheOperator;
|
||||
|
||||
/**
|
||||
* 记录用户登录失败次数的缓存
|
||||
* <p>
|
||||
* key是用户账号,value是登录失败错误次数
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2022/3/15 17:09
|
||||
*/
|
||||
public class LoginErrorCountMemoryCache extends AbstractMemoryCacheOperator<Integer> {
|
||||
|
||||
public LoginErrorCountMemoryCache(TimedCache<String, Integer> timedCache) {
|
||||
super(timedCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommonKeyPrefix() {
|
||||
return LoginCacheConstants.LOGIN_ERROR_CACHE_PREFIX;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package cn.stylefeng.roses.kernel.auth.cache;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.constants.LoginCacheConstants;
|
||||
import cn.stylefeng.roses.kernel.cache.redis.AbstractRedisCacheOperator;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
/**
|
||||
* 记录用户登录失败次数的缓存
|
||||
* <p>
|
||||
* key是用户账号,value是登录失败错误次数
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2022/3/15 17:06
|
||||
*/
|
||||
public class LoginErrorCountRedisCache extends AbstractRedisCacheOperator<Integer> {
|
||||
|
||||
public LoginErrorCountRedisCache(RedisTemplate<String, Integer> redisTemplate) {
|
||||
super(redisTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommonKeyPrefix() {
|
||||
return LoginCacheConstants.LOGIN_ERROR_CACHE_PREFIX;
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package cn.stylefeng.roses.kernel.auth.cache;
|
||||
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import cn.stylefeng.roses.kernel.auth.api.constants.LoginCacheConstants;
|
||||
import cn.stylefeng.roses.kernel.cache.memory.AbstractMemoryCacheOperator;
|
||||
|
||||
/**
|
||||
* 用户帐号冻结的缓存
|
||||
*
|
||||
* @author xixiaowei
|
||||
* @date 2022/1/22 17:33
|
||||
*/
|
||||
public class LoginMemoryCache extends AbstractMemoryCacheOperator<String> {
|
||||
|
||||
public LoginMemoryCache(TimedCache<String, String> timedCache) {
|
||||
super(timedCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommonKeyPrefix() {
|
||||
return LoginCacheConstants.LOGIN_CACHE_PREFIX;
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package cn.stylefeng.roses.kernel.auth.cache;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.constants.LoginCacheConstants;
|
||||
import cn.stylefeng.roses.kernel.cache.redis.AbstractRedisCacheOperator;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
/**
|
||||
* 用户帐号冻结的缓存
|
||||
*
|
||||
* @author xixiaowei
|
||||
* @date 2022/1/23 23:34
|
||||
*/
|
||||
public class LoginRedisCache extends AbstractRedisCacheOperator<String> {
|
||||
|
||||
public LoginRedisCache(RedisTemplate<String, String> redisTemplate) {
|
||||
super(redisTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommonKeyPrefix() {
|
||||
return LoginCacheConstants.LOGIN_CACHE_PREFIX;
|
||||
}
|
||||
}
|
|
@ -3,31 +3,31 @@ package cn.stylefeng.roses.kernel.auth.starter;
|
|||
import cn.hutool.cache.CacheUtil;
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import cn.stylefeng.roses.kernel.auth.api.constants.LoginCacheConstants;
|
||||
import cn.stylefeng.roses.kernel.auth.cache.LoginMemoryCache;
|
||||
import cn.stylefeng.roses.kernel.auth.cache.LoginErrorCountMemoryCache;
|
||||
import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 登录缓存的自动配置
|
||||
* 登录错误次数的缓存
|
||||
*
|
||||
* @author xixiaowei
|
||||
* @date 2022/1/22 17:40
|
||||
* @author fengshuonan
|
||||
* @date 2022/3/15 17:26
|
||||
*/
|
||||
@Configuration
|
||||
public class GunsLoginCacheAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 登录帐号冻结的缓存
|
||||
* 登录错误次数的缓存
|
||||
*
|
||||
* @author xixiaowei
|
||||
* @date 2022/1/22 17:45
|
||||
* @author fengshuonan
|
||||
* @date 2022/3/15 17:25
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "loginCacheOperatorApi")
|
||||
public CacheOperatorApi<String> loginCacheOperatorApi() {
|
||||
TimedCache<String, String> loginTimeCache = CacheUtil.newTimedCache(LoginCacheConstants.LOGIN_CACHE_TIMEOUT_SECONDS * 1000);
|
||||
return new LoginMemoryCache(loginTimeCache);
|
||||
@ConditionalOnMissingBean(name = "loginErrorCountCacheApi")
|
||||
public CacheOperatorApi<Integer> loginErrorCountCacheApi() {
|
||||
TimedCache<String, Integer> loginTimeCache = CacheUtil.newTimedCache(LoginCacheConstants.LOGIN_CACHE_TIMEOUT_SECONDS * 1000);
|
||||
return new LoginErrorCountMemoryCache(loginTimeCache);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
package cn.stylefeng.roses.kernel.db.starter;
|
||||
|
||||
import cn.stylefeng.roses.kernel.db.api.pojo.druid.DruidProperties;
|
||||
import com.alibaba.druid.util.Utils;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 去除druid底部广告
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2022/3/15 16:40
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
|
||||
public class RemoveDruidAdConfig {
|
||||
public class GunsRemoveDruidAdAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 除去页面底部的广告
|
||||
|
@ -21,7 +27,8 @@ public class RemoveDruidAdConfig {
|
|||
* @date 2022/1/24 15:23
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean removeDruidAdFilterRegistrationBean() {
|
||||
public FilterRegistrationBean<?> removeDruidAdFilterRegistrationBean() {
|
||||
|
||||
// 提取common.js的配置路径
|
||||
String pattern = "/druid/*";
|
||||
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
|
||||
|
@ -31,7 +38,7 @@ public class RemoveDruidAdConfig {
|
|||
//创建filter进行过滤
|
||||
Filter filter = new Filter() {
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
public void init(FilterConfig filterConfig) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,7 +58,8 @@ public class RemoveDruidAdConfig {
|
|||
public void destroy() {
|
||||
}
|
||||
};
|
||||
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
|
||||
|
||||
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(filter);
|
||||
registrationBean.addUrlPatterns(commonJsPattern);
|
||||
return registrationBean;
|
|
@ -2,4 +2,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
|||
cn.stylefeng.roses.kernel.db.starter.GunsDataSourceAutoConfiguration,\
|
||||
cn.stylefeng.roses.kernel.db.starter.GunsDruidPropertiesAutoConfiguration,\
|
||||
cn.stylefeng.roses.kernel.db.starter.GunsMyBatisPlusAutoConfiguration,\
|
||||
cn.stylefeng.roses.kernel.db.starter.GunsDruidMonitorAutoConfiguration
|
||||
cn.stylefeng.roses.kernel.db.starter.GunsDruidMonitorAutoConfiguration,\
|
||||
cn.stylefeng.roses.kernel.db.starter.GunsRemoveDruidAdAutoConfiguration
|
||||
|
|
|
@ -114,11 +114,11 @@
|
|||
</dependency>
|
||||
|
||||
<!--登录sdk-->
|
||||
<!--<dependency>-->
|
||||
<!-- <groupId>cn.stylefeng.roses</groupId>-->
|
||||
<!-- <artifactId>auth-sdk</artifactId>-->
|
||||
<!-- <version>${roses.version}</version>-->
|
||||
<!--</dependency>-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>auth-sdk</artifactId>
|
||||
<version>${roses.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--web模块-->
|
||||
<dependency>
|
||||
|
@ -126,20 +126,6 @@
|
|||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--jwt-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>jwt-api</artifactId>
|
||||
<version>${roses.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--message-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>message-api</artifactId>
|
||||
<version>${roses.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
Loading…
Reference in New Issue