敏感词过滤“gsdl”“sbd”和新增账户锁定功能

This commit is contained in:
liang.chao 2025-04-21 13:33:53 +08:00
parent d980d27cb9
commit 2681ea00d4
4 changed files with 280 additions and 76 deletions

View File

@ -0,0 +1,50 @@
package com.bonus.gs.sub.evaluate.filter;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
/**
* @Authorliang.chao
* @Date2025/4/21 - 10:22
*/
@Component
public class SensitiveWordFilter {
private static final Set<String> sensitiveWords = new HashSet<>();
static {
// 初始化敏感词列表
sensitiveWords.add("gsdl");
sensitiveWords.add("sbd");
}
public static String repeatChar(char c, int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(c);
}
return sb.toString();
}
/**
* 过滤敏感词
*
* @param text 输入文本
* @return 替换后的文本
*/
public static String filter(String text) {
if (text == null || text.isEmpty()) {
return text;
}
for (String word : sensitiveWords) {
if (text.contains(word)) {
// 将敏感词替换为*
text = text.replaceAll(word, repeatChar('*', word.length()));
}
}
return text;
}
}

View File

@ -0,0 +1,49 @@
package com.bonus.gs.sub.evaluate.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.Map;
/**
* @Authorliang.chao
* @Date2025/4/21 - 10:26
*/
@ControllerAdvice
public class SensitiveWordResponseInterceptor implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
// 返回true表示对所有响应都进行处理可以根据需要添加条件
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof String) {
// 如果是String类型直接过滤
return SensitiveWordFilter.filter((String) body);
} else if (body != null) {
// 如果是复杂对象转换为JSON字符串进行过滤需要依赖JSON库如Jackson
try {
String jsonString = new ObjectMapper().writeValueAsString(body);
String filteredJsonString = SensitiveWordFilter.filter(jsonString);
// 注意这里简单处理为返回字符串实际项目中可能需要反序列化回对象或使用其他方式处理
// 这里仅为演示实际可能需要自定义处理逻辑
return new ObjectMapper().readValue(filteredJsonString, Object.class); // 这行在实际中可能需要更复杂的处理
// 简单处理可以直接返回字符串如果前端能处理或修改为返回Map/自定义对象
} catch (Exception e) {
e.printStackTrace();
}
}
// 对于非String且非需要复杂处理的对象直接返回
return body;
}
}

View File

@ -0,0 +1,87 @@
package com.bonus.gs.sub.evaluate.manager.config;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Authorliang.chao
* @Date2025/4/21 - 11:24
*/
@Service
public class LoginAttemptService {
private final int MAX_ATTEMPTS = 3; // 最大允许失败次数
private static final long LOCK_TIME_IN_MINUTES = 30; // 锁定时间分钟
private final ConcurrentHashMap<String, LoginAttempt> userAttempts = new ConcurrentHashMap<>();
/**
* 处理登录失败逻辑
*/
public void handleLoginFailure(String username) {
LoginAttempt attempt = userAttempts.getOrDefault(username, new LoginAttempt());
if (!attempt.isLocked()) {
attempt.incrementAttempts();
if (attempt.getAttempts() >= MAX_ATTEMPTS) {
attempt.lockAccount(); // 锁定账号
}
userAttempts.put(username, attempt);
}
}
/**
* 检查账号是否被锁定
*/
public boolean isAccountLocked(String username) {
LoginAttempt attempt = userAttempts.get(username);
if (attempt == null || !attempt.isLocked()) {
return false;
}
// 检查锁定时间是否已过期
if (LocalDateTime.now().isAfter(attempt.getLockTime())) {
unlockAccount(username); // 自动解锁
return false;
}
return true;
}
/**
* 解锁账号
*/
public void unlockAccount(String username) {
userAttempts.remove(username);
}
/**
* 内部类记录用户的登录尝试信息
*/
private static class LoginAttempt {
private int attempts = 0;
private LocalDateTime lockTime;
private boolean locked = false;
public void incrementAttempts() {
this.attempts++;
}
public int getAttempts() {
return attempts;
}
public void lockAccount() {
this.locked = true;
this.lockTime = LocalDateTime.now().plusMinutes(LOCK_TIME_IN_MINUTES); // 设置锁定时间
}
public boolean isLocked() {
return locked;
}
public LocalDateTime getLockTime() {
return lockTime;
}
}
}

View File

@ -27,7 +27,6 @@ import com.bonus.gs.sub.evaluate.manager.utils.ResponseUtil;
/**
* spring security处理器
*
*/
@Configuration
public class SecurityHandlerConfig {
@ -35,6 +34,9 @@ public class SecurityHandlerConfig {
@Autowired
private TokenService tokenService;
@Autowired
private LoginAttemptService loginAttemptService;
/**
* 登陆成功返回Token
*
@ -49,9 +51,16 @@ public class SecurityHandlerConfig {
Authentication authentication) throws IOException, ServletException {
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 检查账号是否被锁定
if (loginAttemptService.isAccountLocked(loginUser.getUsername())) {
throw new BadCredentialsException("账号已锁定请30分钟后重试");
} else {
Token token = tokenService.saveToken(loginUser);
// 登录成功时重置失败计数
loginAttemptService.unlockAccount(loginUser.getUsername());
ResponseUtil.responseJson(response, HttpStatus.OK.value(), token);
}
}
};
}
@ -67,9 +76,19 @@ public class SecurityHandlerConfig {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String msg = null;
String username = request.getParameter("username"); // 获取用户名
String msg;
if (exception instanceof BadCredentialsException) {
msg = "密码错误";
// 处理登录失败逻辑
loginAttemptService.handleLoginFailure(username);
// 检查账号是否被锁定
if (loginAttemptService.isAccountLocked(username)) {
msg = "账号已锁定请30分钟后重试";
}
} else {
msg = exception.getMessage();
}
@ -77,7 +96,6 @@ public class SecurityHandlerConfig {
ResponseUtil.responseJson(response, HttpStatus.UNAUTHORIZED.value(), info);
}
};
}
/**