diff --git a/base/src/main/java/com/tinyengine/it/login/utils/JwtUtil.java b/base/src/main/java/com/tinyengine/it/login/utils/JwtUtil.java index 8b6bce17..41f8a0e4 100644 --- a/base/src/main/java/com/tinyengine/it/login/utils/JwtUtil.java +++ b/base/src/main/java/com/tinyengine/it/login/utils/JwtUtil.java @@ -17,21 +17,25 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.MalformedJwtException; -import io.jsonwebtoken.security.Keys; -import jakarta.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.crypto.SecretKey; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * Jwt util @@ -40,38 +44,53 @@ @Slf4j public class JwtUtil { - @Autowired - private TokenBlacklistService tokenBlacklistService; - - private static final long EXPIRATION_TIME = 21600000L; // 6小时 = 6 * 60 * 60 * 1000 = 21600000 毫秒 - private static final String SECRET_ENV_NAME = "SECRET_STRING"; - - @PostConstruct - public void validateSecretConfiguration() { - try { - getSecretKey(); - } catch (Exception e) { - throw new IllegalStateException( - "JWT secret is not configured correctly. Set environment variable " - + SECRET_ENV_NAME + " to a strong value before starting the service.", - e - ); - } - } - - private static String getSecretString() { - String secret = System.getenv(SECRET_ENV_NAME); - if (secret == null || secret.isBlank()) { - throw new IllegalStateException( - "Missing required environment variable " + SECRET_ENV_NAME + " for JWT signing." - ); - } - return secret; - } - - public static SecretKey getSecretKey() { - return Keys.hmacShaKeyFor(getSecretString().getBytes(StandardCharsets.UTF_8)); - } + @Autowired + private TokenBlacklistService tokenBlacklistService; + + @Autowired + private Environment environment; + + private static final long EXPIRATION_TIME = 21600000L; // 6小时 = 6 * 60 * 60 * 1000 = 21600000 毫秒 + private static final String SECRET_ENV_NAME = "SECRET_STRING"; + private static String cachedSecret; + + @PostConstruct + public void validateSecretConfiguration() { + boolean isDev = Arrays.asList(environment.getActiveProfiles()).contains("dev"); + String secret = resolveSecret(isDev); + cachedSecret = secret; + if (isDev && System.getenv(SECRET_ENV_NAME) == null) { + log.info("JWT secret auto-generated for dev profile (tokens invalidate on restart)"); + } + try { + getSecretKey(); + } catch (Exception e) { + throw new IllegalStateException( + "JWT secret is not configured correctly. Set environment variable " + + SECRET_ENV_NAME + " to a strong value before starting the service.", + e + ); + } + } + + private static String resolveSecret(boolean isDev) { + String secret = System.getenv(SECRET_ENV_NAME); + if (secret != null && !secret.isBlank()) { + return secret; + } + if (isDev) { + byte[] bytes = new byte[32]; + new SecureRandom().nextBytes(bytes); + return Base64.getEncoder().encodeToString(bytes); + } + throw new IllegalStateException( + "Missing required environment variable " + SECRET_ENV_NAME + " for JWT signing." + ); + } + + public static SecretKey getSecretKey() { + return Keys.hmacShaKeyFor(cachedSecret.getBytes(StandardCharsets.UTF_8)); + } /** * 生成包含完整用户信息的 JWT Token(支持 Tenant 对象和 Map 两种格式)