Files
UserBaseService/src/main/java/com/yunhai/userbase/config/UserBaseAppSecretProvider.java
T
chenxing a5ced786b4
ci-userbase-service / build-and-push (push) Failing after 1m45s
组用户功能修订
2026-08-12 19:16:22 +08:00

99 lines
3.4 KiB
Java

package com.yunhai.userbase.config;
import com.yunhai.platform.appauth.AppAuthProperties;
import com.yunhai.platform.appauth.AppSecretProvider;
import com.yunhai.userbase.entity.ApplicationInfo;
import com.yunhai.userbase.repository.ApplicationInfoRepository;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* UserBase 作为「应用密钥权威源」的验签密钥提供方。<br>
* <br>
* 解析优先级:<br>
* 1) 本库 application_info —— 覆盖全部「已颁发」应用,新增/重置密钥后即时生效;<br>
* 2) 配置式受信应用 {@code yh.app-auth.trusted-apps} —— 平台自身默认密钥(如 应用中心 100616)
* 兜底,使其即便尚未落库也可验签,避免「默认 appKey 未建行」导致验签失败。<br>
* <br>
* 带 60s 本地 TTL 缓存,避免每次验签直打库(密钥变更最长 60s 后生效)。<br>
* <br>
* 这样 UserBase 作为颁发方不依赖 Redis 预热、也不局限于 trustedApps 写死的少量密钥,
* 能够验签所有它已颁发的应用。
*/
public class UserBaseAppSecretProvider implements AppSecretProvider {
private final ApplicationInfoRepository repository;
private final Map<String, String> trustedApps;
private final ConcurrentHashMap<String, CacheEntry> cache = new ConcurrentHashMap<>();
private static final long TTL_MS = 60_000L;
public UserBaseAppSecretProvider(ApplicationInfoRepository repository, AppAuthProperties properties) {
this.repository = repository;
this.trustedApps = properties.getTrustedApps();
}
@Override
public String getSecret(String appKey) {
if (appKey == null) {
return null;
}
long now = System.currentTimeMillis();
CacheEntry hit = cache.get(appKey);
if (hit != null && now - hit.ts < TTL_MS) {
return hit.secret;
}
String secret = null;
try {
Integer id = parse(appKey);
if (id != null) {
ApplicationInfo info = repository.findByAppKey(id);
if (info != null && info.getAppSecret() != null && !info.getAppSecret().trim().isEmpty()) {
secret = info.getAppSecret();
}
}
} catch (Exception ignored) {
// 查询异常不阻断,继续走配置兜底
}
if (secret == null) {
secret = trustedApps.get(appKey);
}
cache.put(appKey, new CacheEntry(now, secret));
return secret;
}
/**
* 主动失效:立刻清除本地缓存中指定 appKey 的条目,密钥重置后调用 → 下一次 getSecret 直查 DB 拿新值,
* 不再受最长 60s TTL 生效窗口限制。
*/
public void invalidate(String appKey) {
if (appKey == null) {
return;
}
cache.remove(appKey);
}
/** 主动失效:立刻清空全部本地缓存(refreshAll / Redis 全量对账时调用)。 */
public void invalidateAll() {
cache.clear();
}
private static Integer parse(String appKey) {
try {
return Integer.parseInt(appKey.trim());
} catch (NumberFormatException e) {
return null;
}
}
private static final class CacheEntry {
final long ts;
final String secret;
CacheEntry(long ts, String secret) {
this.ts = ts;
this.secret = secret;
}
}
}