敏感词过滤“gsdl”“sbd”和新增账户锁定功能
This commit is contained in:
parent
d980d27cb9
commit
2681ea00d4
|
|
@ -0,0 +1,50 @@
|
|||
package com.bonus.gs.sub.evaluate.filter;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @Author:liang.chao
|
||||
* @Date:2025/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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* @Author:liang.chao
|
||||
* @Date:2025/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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* @Author:liang.chao
|
||||
* @Date:2025/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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue