This commit is contained in:
雷欧(林平凡)
2025-06-17 18:50:02 +08:00
parent faab92e9b5
commit e0f9952773
7 changed files with 59 additions and 10 deletions

View File

@@ -10,6 +10,7 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
@@ -39,7 +40,7 @@ public class SecurityConfig {
provider.setPasswordEncoder(passwordEncoder);
http
.csrf(csrf -> csrf.disable())
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)

View File

@@ -15,6 +15,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;
@@ -41,7 +42,7 @@ public class AuthController {
private static final String USER_TOKEN_PREFIX = "user:token:";
@PostMapping("/login")
public ApiResponse<LoginResponse> login(@RequestBody LoginRequest request) {
public ApiResponse login(@RequestBody LoginRequest request) {
logger.info("用户登录");
logger.info("用户名:{}", request.getUsername());
logger.info("密码:{}", request.getPassword());
@@ -49,28 +50,39 @@ public class AuthController {
logger.info("生成的验证码:{}", request.getGeneratedCaptcha());
// 1. 基础校验
if (StringUtils.isBlank(request.getUsername()) || StringUtils.isBlank(request.getPassword())) {
throw new RuntimeException("用户名或密码不能为空");
logger.error("用户名或密码不能为空");
return ApiResponse.badRequest();
}
// 2. 验证码校验
if (!captchaService.validateCaptcha(request.getCaptcha(), request.getGeneratedCaptcha())) {
throw new RuntimeException("验证码错误");
logger.error("验证码错误");
return ApiResponse.badRequest();
}
// 在验证码校验后、认证前添加防爆破逻辑
// 在验证码校验后、认证前添加防爆破逻辑
String loginFailKey = "login:fail:" + request.getUsername();
Long failCount = redisTemplate.opsForValue().increment(loginFailKey);
if (failCount != null && failCount > 5) {
throw new RuntimeException("尝试次数过多,请稍后再试");
logger.error("尝试次数过多,请稍后再试");
return ApiResponse.badRequest();
}else {
redisTemplate.expire(loginFailKey, 5, TimeUnit.MINUTES);
}
// 3. 用户认证Spring Security 标准流程)
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
logger.info("3. 用户认证Spring Security 标准流程)");
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
} catch (Exception e) {
logger.error("用户认证失败:{}", e.getMessage());
}
// 4. 生成 JWT Token
logger.info("用户认证成功");
String token = jwtUtils.generateToken(request.getUsername());
String refreshToken = generateRefreshToken(request.getUsername());
logger.info("生成 JWT Token{}", token);
// 5. 存入 Redis
saveUserToken(request.getUsername(), token, jwtUtils.getExpiration(token));

View File

@@ -31,6 +31,7 @@ public class JwtAuthFilter extends OncePerRequestFilter {
@NonNull FilterChain filterChain)
throws ServletException, IOException {
try {
logger.debug("JwtAuthFilter.doFilterInternal");
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateToken(jwt)) {
String username = jwtUtils.extractUsername(jwt);

View File

@@ -1,6 +1,8 @@
package cn.van.business.service;
import cn.hutool.crypto.digest.MD5;
import cn.van.business.model.user.AdminUser;
import cn.van.business.util.Util;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@@ -19,7 +21,8 @@ public class AdminUserService {
// 初始化一个测试用户(生产环境应从数据库加载)
AdminUser user = new AdminUser();
user.setUsername("van");
user.setPassword(new BCryptPasswordEncoder().encode("LK.807878712"));
String password = Util.md5("LK.807878712");
user.setPassword(new BCryptPasswordEncoder().encode(password));
users.put("van", user);
}

View File

@@ -14,7 +14,7 @@ import java.util.Date;
@Component
public class JwtUtils {
private final String secret = "your-secret-key"; // 应配置在 application.yml 中
private final String secret = "7b78f5cc9735091442361c78b863607d"; // 应配置在 application.yml 中
private final long expiration = 86400000 * 7; // 24小时
/**

View File

@@ -13,6 +13,8 @@ import org.springframework.web.util.HtmlUtils;
import java.io.*;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
/**
@@ -23,6 +25,36 @@ Util {
private static final Logger log = LoggerFactory.getLogger(Util.class);
/**
* 将字符串转换为 MD5 摘要
*
* @param input 原始字符串
* @return MD5 加密后的十六进制字符串
*/
public static String md5(String input) {
try {
// 创建 MessageDigest 实例,指定 MD5 算法
MessageDigest md = MessageDigest.getInstance("MD5");
// 将输入字符串转换为字节数组并进行哈希计算
byte[] messageDigest = md.digest(input.getBytes());
// 将字节数组转换为十六进制字符串
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1)
hexString.append('0'); // 补零
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5加密失败", e);
}
}
/**
* byte数组倒序
*