【7.2.2】更新登录重试错误次数限制机制

pull/29/head
fengshuonan 2022-03-15 18:45:40 +08:00
parent fc783bbdf6
commit 6995c44b69
12 changed files with 108 additions and 140 deletions

View File

@ -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;
}

View File

@ -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", "密码重试次数过多,帐号被锁定");
/**
*

View File

@ -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>

View File

@ -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());
}
}

View File

@ -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>
* keyvalue
*
* @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;
}
}

View File

@ -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>
* keyvalue
*
* @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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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

View File

@ -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>