feat: 添加用户登录服务及JWT认证功能

refactor: 重构实体类并添加Lombok注解

docs: 更新数据库表结构文档

style: 清理无用代码并优化格式

fix: 修复用户详情服务中的联系方式更新方法

build: 更新pom.xml配置并添加Lombok插件

test: 添加用户登录测试用例

chore: 添加开发和生产环境配置文件
This commit is contained in:
qingfeng1121
2025-11-28 14:14:38 +08:00
parent 7536c8087e
commit 51086db30e
64 changed files with 6168 additions and 567 deletions

View File

@@ -0,0 +1,120 @@
package com.qf.backend.util;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import javax.crypto.SecretKey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
/**
* JWT工具类用于生成和验证tokenJJWT 0.12.x 版本)
* 支持 Java 17+ 和 Jakarta EESpring Boot 3.x
*/
@Component
public class JwtUtils {
@Value("${jwt.secret:default_secret_key_for_development}")
private String secret;
@Value("${jwt.expiration:3600000}")
private long expiration;
@Value("${jwt.token-prefix:Bearer}")
private String tokenPrefix;
/**
* 获取签名密钥
*/
private SecretKey getSignInKey() {
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
return Keys.hmacShaKeyFor(keyBytes);
}
/**
* 从token中获取用户名
*/
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
/**
* 从token中获取过期时间
*/
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
/**
* 从token中获取指定的claim
*/
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
/**
* 获取token中的所有claims
*/
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.verifyWith(getSignInKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
/**
* 检查token是否过期
*/
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
/**
* 生成token
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("authorities", userDetails.getAuthorities());
return doGenerateToken(claims, userDetails.getUsername());
}
/**
* 生成token的核心方法
*/
private String doGenerateToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.claims(claims) // 使用新的 claims() 方法
.subject(subject) // 使用新的 subject() 方法
.issuedAt(new Date(System.currentTimeMillis()))
.expiration(new Date(System.currentTimeMillis() + expiration)) // 使用新的 expiration() 方法
.signWith(getSignInKey(), Jwts.SIG.HS256) // 使用新的签名方式
.compact();
}
/**
* 验证token
*/
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
/**
* 获取token前缀
*/
public String getTokenPrefix() {
return tokenPrefix;
}
}