Files
my_site/backend/src/main/java/com/yoyuzh/auth/JwtTokenProvider.java

122 lines
4.2 KiB
Java

package com.yoyuzh.auth;
import com.yoyuzh.config.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Date;
import org.springframework.util.StringUtils;
@Component
public class JwtTokenProvider {
private static final String DEFAULT_SECRET = "change-me-change-me-change-me-change-me";
private final JwtProperties jwtProperties;
private SecretKey secretKey;
public JwtTokenProvider(JwtProperties jwtProperties) {
this.jwtProperties = jwtProperties;
}
@PostConstruct
public void init() {
String secret = jwtProperties.getSecret() == null ? "" : jwtProperties.getSecret().trim();
if (secret.isEmpty()) {
throw new IllegalStateException("app.jwt.secret 未配置,请设置强密钥后再启动");
}
if (DEFAULT_SECRET.equals(secret)) {
throw new IllegalStateException("检测到默认 JWT 密钥,请替换 app.jwt.secret 后再启动");
}
if (secret.getBytes(StandardCharsets.UTF_8).length < 32) {
throw new IllegalStateException("JWT 密钥长度过短,至少需要 32 字节");
}
secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
}
public String generateAccessToken(Long userId, String username, String sessionId) {
return generateAccessToken(userId, username, sessionId, AuthClientType.DESKTOP);
}
public String generateAccessToken(Long userId, String username, String sessionId, AuthClientType clientType) {
Instant now = Instant.now();
var builder = Jwts.builder()
.subject(username)
.claim("uid", userId)
.claim("client", clientType.name())
.issuedAt(Date.from(now))
.expiration(Date.from(now.plusSeconds(jwtProperties.getAccessExpirationSeconds())))
.signWith(secretKey);
if (StringUtils.hasText(sessionId)) {
builder.claim("sid", sessionId);
}
return builder.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token);
return true;
} catch (Exception ex) {
return false;
}
}
public String getUsername(String token) {
return parseClaims(token).getSubject();
}
public Long getUserId(String token) {
Object uid = parseClaims(token).get("uid");
return uid == null ? null : Long.parseLong(uid.toString());
}
public Instant getIssuedAt(String token) {
Date issuedAt = parseClaims(token).getIssuedAt();
return issuedAt == null ? null : issuedAt.toInstant();
}
public String getSessionId(String token) {
Object sessionId = parseClaims(token).get("sid");
return sessionId == null ? null : sessionId.toString();
}
public AuthClientType getClientType(String token) {
Object clientType = parseClaims(token).get("client");
return AuthClientType.fromHeader(clientType == null ? null : clientType.toString());
}
public boolean hasMatchingSession(String token, String activeSessionId) {
String tokenSessionId = getSessionId(token);
if (!StringUtils.hasText(activeSessionId)) {
return !StringUtils.hasText(tokenSessionId);
}
return activeSessionId.equals(tokenSessionId);
}
public boolean hasMatchingSession(String token, User user) {
String expectedSessionId = switch (getClientType(token)) {
case MOBILE -> user.getMobileActiveSessionId();
case DESKTOP -> StringUtils.hasText(user.getDesktopActiveSessionId())
? user.getDesktopActiveSessionId()
: user.getActiveSessionId();
};
return hasMatchingSession(token, expectedSessionId);
}
private Claims parseClaims(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload();
}
}