package com.wok.supportbot.security; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * SDK 专用 JWT 令牌提供者 * 独立于管理后台 JwtTokenProvider,使用独立密钥签发和验证 SDK Token。 *
* Token 中包含:
* - sub: apiKeyId(API Key 标识)
* - ak: 脱敏后的 API Key
* - rids: 允许使用的客服角色 ID 列表
*/
@Slf4j
@Component
public class SdkJwtTokenProvider {
private final SecretKey key;
private final long defaultExpiration;
/** 24 小时上限 */
private static final long MAX_EXPIRATION = 86400000L;
/** 5 分钟下限 */
private static final long MIN_EXPIRATION = 300000L;
/** 默认密钥标识(禁止使用) */
private static final String DEFAULT_SECRET = "support-bot-sdk-jwt-secret-2026-please-change";
public SdkJwtTokenProvider(
@Value("${jwt.sdk-secret:support-bot-sdk-jwt-secret-2026-please-change}") String sdkSecret,
@Value("${jwt.sdk-expiration:7200000}") long defaultExpiration) {
if (DEFAULT_SECRET.equals(sdkSecret)) {
log.warn("⚠️ SDK JWT 使用了默认密钥,存在安全风险!请在 application.yml 中配置 jwt.sdk-secret 为强随机字符串");
}
this.key = Keys.hmacShaKeyFor(sdkSecret.getBytes(StandardCharsets.UTF_8));
this.defaultExpiration = defaultExpiration;
}
/**
* 签发 SDK JWT Token
*
* @param apiKeyId API Key 标识
* @param maskedApiKey 脱敏后的 API Key
* @param roleIds 允许使用的客服角色 ID 列表
* @param expirationMs 过期时间(毫秒),会被钳制到 [5min, 24h]
* @return JWT Token 字符串
*/
public String generateToken(String apiKeyId, String maskedApiKey, List