cl
5 天以前 01ab61191b93956954b463ab4416fda6b5f960ee
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/TokenServiceImpl.java
@@ -1,26 +1,65 @@
package com.vincent.rsf.openApi.service.impl;
import com.vincent.rsf.openApi.entity.constant.Constants;
import com.vincent.rsf.openApi.security.service.AppAuthService;
import com.vincent.rsf.openApi.service.TokenService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
 * 对接 Token:JWT(密钥来自配置,重启后同一密钥仍可校验未过期 token;claims 仅含 appId)
 */
@Slf4j
@Service
public class TokenServiceImpl implements TokenService {
    private static final long EXPIRE_MS = 60 * 60 * 1000L;
    private static final String CLAIM_APP_ID = "appId";
    private final AppAuthService appAuthService;
    private final SecretKey secretKey;
    private final long expireMs;
    @Autowired
    private AppAuthService appAuthService;
    public TokenServiceImpl(
            AppAuthService appAuthService,
            @Value("${open-api.jwt.secret:}") String jwtSecret,
            @Value("${open-api.jwt.expire-seconds:3600}") long expireSeconds) {
        this.appAuthService = appAuthService;
        if (!StringUtils.hasText(jwtSecret)) {
            throw new IllegalStateException("请配置 open-api.jwt.secret(HS256,建议至少 32 字符或使用环境变量覆盖)");
        }
        this.secretKey = hmacSha256Key(jwtSecret.trim());
        this.expireMs = expireSeconds * 1000L;
    }
    private final Map<String, Long> tokenExpire = new ConcurrentHashMap<>();
    /** 短字符串用 SHA-256 派生 32 字节以满足 HS256 */
    private static SecretKey hmacSha256Key(String secret) {
        byte[] bytes = secret.getBytes(StandardCharsets.UTF_8);
        if (bytes.length < 32) {
            try {
                MessageDigest md = MessageDigest.getInstance("SHA-256");
                bytes = md.digest(bytes);
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
        return Keys.hmacShaKeyFor(bytes);
    }
    @Override
    public String issueToken(String appId, String appSecret) {
@@ -30,27 +69,43 @@
        if (!appAuthService.validateApp(appId, appSecret)) {
            return null;
        }
        String token = UUID.randomUUID().toString().replace("-", "");
        tokenExpire.put(token, System.currentTimeMillis() + EXPIRE_MS);
        evictExpired();
        return token;
        long now = System.currentTimeMillis();
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_APP_ID, appId);
        String compact = Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(new Date(now))
                .setExpiration(new Date(now + expireMs))
                .signWith(secretKey, SignatureAlgorithm.HS256)
                .compact();
        return Constants.TOKEN_PREFIX + compact;
    }
    @Override
    public boolean validateToken(String token) {
        if (!StringUtils.hasText(token)) {
            return false;
        }
        Long expire = tokenExpire.get(token);
        if (expire == null || System.currentTimeMillis() > expire) {
            tokenExpire.remove(token);
            return false;
        }
        return true;
        return getAppIdIfValid(token) != null;
    }
    private void evictExpired() {
        long now = System.currentTimeMillis();
        tokenExpire.entrySet().removeIf(e -> e.getValue() < now);
    @Override
    public String getAppIdIfValid(String rawToken) {
        if (!StringUtils.hasText(rawToken)) {
            return null;
        }
        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(secretKey)
                    .build()
                    .parseClaimsJws(rawToken)
                    .getBody();
            Date exp = claims.getExpiration();
            if (exp == null || exp.before(new Date())) {
                return null;
            }
            Object appId = claims.get(CLAIM_APP_ID);
            return appId != null ? appId.toString() : null;
        } catch (JwtException e) {
            log.debug("JWT 校验失败: {}", e.getMessage());
            return null;
        }
    }
}