【7.1.6】去除druid广告&增加密码错误次数限定

pull/26/head
xixiaowei 2022-01-24 00:29:38 +08:00
parent 79557723fe
commit bc352d374a
15 changed files with 312 additions and 13 deletions

View File

@ -114,4 +114,11 @@ public interface AuthServiceApi {
*/
void checkAuth(String token, String requestUrl);
/**
*
*
* @author xixiaowei
* @date 2022/1/22 16:37
*/
void cancelFreeze(LoginRequest loginRequest);
}

View File

@ -0,0 +1,25 @@
package cn.stylefeng.roses.kernel.auth.api.constants;
/**
*
*
* @author xixiaowei
* @date 2022/1/22 17:37
*/
public interface LoginCacheConstants {
/**
*
*/
Integer MAX_LOGIN_COUNT = 5;
/**
*
*/
String LOGIN_CACHE_PREFIX = "login:";
/**
*
*/
Long LOGIN_CACHE_TIMEOUT_SECONDS = 1800L;
}

View File

@ -121,7 +121,12 @@ public enum AuthExceptionEnum implements AbstractExceptionEnum {
/**
* 访
*/
CANT_REQUEST_UN_OPEN_API(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "17", "无法访问未经授权的接口未授权url为{}");
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", "超过最大登录次数,帐号被锁定");
/**
*

View File

@ -126,6 +126,11 @@
<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

@ -24,6 +24,7 @@
*/
package cn.stylefeng.roses.kernel.auth.auth;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.CharsetUtil;
@ -36,6 +37,7 @@ import cn.hutool.http.HttpResponse;
import cn.stylefeng.roses.kernel.auth.api.AuthServiceApi;
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
import cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants;
import cn.stylefeng.roses.kernel.auth.api.constants.LoginCacheConstants;
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;
@ -47,6 +49,9 @@ import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginWithTokenRequest;
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
import cn.stylefeng.roses.kernel.auth.session.cache.loginuser.RedisLoginUserCache;
import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi;
import cn.stylefeng.roses.kernel.cache.memory.operator.DefaultMemoryCacheOperator;
import cn.stylefeng.roses.kernel.demo.expander.DemoConfigExpander;
import cn.stylefeng.roses.kernel.jwt.JwtTokenOperator;
import cn.stylefeng.roses.kernel.jwt.api.context.JwtContext;
@ -66,10 +71,13 @@ 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;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@ -119,6 +127,15 @@ public class AuthServiceImpl implements AuthServiceApi {
@Resource
private ResourceServiceApi resourceServiceApi;
@Resource
private SysUserService sysUserService;
@Resource
private CacheOperatorApi<String> loginCacheOperatorApi;
@Value("${login.enable}")
private Boolean enable;
@Override
public LoginResponse login(LoginRequest loginRequest) {
return loginAction(loginRequest, true, null);
@ -254,6 +271,25 @@ 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 (enable) {
// 判断错误次数,超过最大放入缓存中
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) {
@ -275,6 +311,8 @@ 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);
}
}
@ -288,6 +326,8 @@ 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);
}
}
@ -308,13 +348,15 @@ 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(), "帐号或密码错误");
throw new AuthException(AuthExceptionEnum.USERNAME_PASSWORD_ERROR);
}
}
@ -324,9 +366,6 @@ 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);
@ -355,6 +394,10 @@ 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());
}
@ -403,4 +446,13 @@ public class AuthServiceImpl implements AuthServiceApi {
return loginCode;
}
@Override
public void cancelFreeze(LoginRequest loginRequest) {
SysUser sysUser = sysUserService.getUserByAccount(loginRequest.getAccount());
sysUser.setLoginCount(1);
// 修改数据库中的登录次数
sysUserService.updateById(sysUser);
// 删除缓存中的数据
loginCacheOperatorApi.remove(sysUser.getUserId().toString());
}
}

View File

@ -0,0 +1,23 @@
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

@ -0,0 +1,23 @@
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

@ -0,0 +1,22 @@
package cn.stylefeng.roses.kernel.auth.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
*
*
* @author xixiaowei
* @date 2022/1/23 23:42
*/
@Data
@Component
@ConfigurationProperties(prefix = "login")
public class AccountErrorDetectionConfig {
/**
* true-false-
*/
private Boolean enable;
}

View File

@ -0,0 +1,33 @@
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.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
*/
@Configuration
public class GunsLoginCacheAutoConfiguration {
/**
*
*
* @author xixiaowei
* @date 2022/1/22 17:45
*/
@Bean
@ConditionalOnMissingBean(name = "loginCacheOperatorApi")
public CacheOperatorApi<String> loginCacheOperatorApi() {
TimedCache<String, String> loginTimeCache = CacheUtil.newTimedCache(LoginCacheConstants.LOGIN_CACHE_TIMEOUT_SECONDS * 1000);
return new LoginMemoryCache(loginTimeCache);
}
}

View File

@ -1,3 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.stylefeng.roses.kernel.auth.starter.GunsAuthAutoConfiguration,\
cn.stylefeng.roses.kernel.auth.starter.GunsSsoAutoConfiguration
cn.stylefeng.roses.kernel.auth.starter.GunsSsoAutoConfiguration,\
cn.stylefeng.roses.kernel.auth.starter.GunsLoginCacheAutoConfiguration

View File

@ -38,6 +38,11 @@
<version>${roses.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,68 @@
package cn.stylefeng.roses.kernel.db.starter;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
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;
@Configuration
@ConditionalOnWebApplication
@AutoConfigureAfter(DruidDataSourceAutoConfigure.class)
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true", matchIfMissing = true)
public class RemoveDruidAdConfig {
/**
* : removeDruidAdFilterRegistrationBean
* : 广
* @param properties
* @return org.springframework.boot.web.servlet.FilterRegistrationBean
* @throws
*/
@Bean
public FilterRegistrationBean removeDruidAdFilterRegistrationBean(DruidStatProperties properties) {
// 获取web监控页面的参数
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
// 提取common.js的配置路径
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
final String filePath = "support/http/resources/js/common.js";
//创建filter进行过滤
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
// 重置缓冲区,响应头不会被重置
response.resetBuffer();
// 获取common.js
String text = Utils.readFromResource(filePath);
// 正则替换banner, 除去底部的广告信息
text = text.replaceAll("<a.*?banner\"></a><br/>", "");
text = text.replaceAll("powered.*?shrek.wang</a>", "");
response.getWriter().write(text);
}
@Override
public void destroy() {
}
};
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns(commonJsPattern);
return registrationBean;
}
}

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,6 +126,20 @@
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jwt-->
<dependency>
<groupId>cn.stylefeng.roses</groupId>
<artifactId>jwt-api</artifactId>
<version>7.1.6</version>
</dependency>
<!--message-->
<dependency>
<groupId>cn.stylefeng.roses</groupId>
<artifactId>message-api</artifactId>
<version>7.1.6</version>
</dependency>
</dependencies>
</project>

View File

@ -143,4 +143,15 @@ public class LoginController {
return new SuccessResponseData<>(haveSessionFlag);
}
/**
*
*
* @author xixiaowei
* @date 2022/1/22 16:40
*/
@PostResource(name = "取消帐号冻结", path = "/cancelFreeze")
public ResponseData<?> cancelFreeze(@RequestBody LoginRequest loginRequest) {
authServiceApi.cancelFreeze(loginRequest);
return new SuccessResponseData<>();
}
}

View File

@ -154,4 +154,9 @@ public class SysUser extends BaseEntity {
@TableField(value = "del_flag", fill = FieldFill.INSERT)
private String delFlag;
/**
*
*/
@TableField("login_count")
private Integer loginCount;
}