You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

K8s扩容Authorization Server后令牌校验异常的解决方法及指南

问题原因与解决方案

核心问题分析

你的问题出在Authorization Server扩容后实例间状态与密钥不共享,具体两点:

  1. 每个实例启动时自动生成独立的RSA密钥对,导致跨实例JWT签名验证不兼容;
  2. 默认使用内存存储令牌授权信息(InMemoryOAuth2AuthorizationService),令牌状态仅存在于生成它的实例本地,其他实例无法查询到。

另外需确认issuer配置是否为统一的负载均衡地址,若每个实例使用自身节点地址,也会导致JWT的iss声明校验失败。


正确扩容方案

1. 统一共享RSA密钥对

停止每个实例自行生成密钥,改用外部存储的共享密钥对,所有实例使用同一套密钥签名和验证JWT。

修改代码加载外部密钥

@Autowired
private OAuthConfig authConfig;

@Autowired
PasswordEncoder passwordEncoder;

@Value("${auth.server.issuer}")
private String issuerAuth;

// 新增密钥路径配置
@Value("${auth.jwt.private-key-path}")
private String privateKeyPath;

@Value("${auth.jwt.public-key-path}")
private String publicKeyPath;

// ... 其他原有Bean保持不变 ...

@Bean
public JWKSource<SecurityContext> jwkSource() throws Exception {
    // 从挂载的密钥文件加载私钥
    RSAPrivateKey privateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA")
            .generatePrivate(new PKCS8EncodedKeySpec(Files.readAllBytes(Paths.get(privateKeyPath))));
    // 加载公钥
    RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
            .generatePublic(new X509EncodedKeySpec(Files.readAllBytes(Paths.get(publicKeyPath))));

    RSAKey rsaKey = new RSAKey.Builder(publicKey)
            .privateKey(privateKey)
            .keyID("shared-auth-key")
            .build();
    JWKSet jwkSet = new JWKSet(rsaKey);
    return new ImmutableJWKSet<>(jwkSet);
}

// ... 其他原有方法保持不变 ...

K8s侧配置密钥存储

生成密钥对并存储为K8s Secret:

# 生成3072位RSA密钥对
openssl genrsa -out private.key 3072
openssl rsa -in private.key -pubout -out public.key

# 创建Secret存储密钥
kubectl create secret generic auth-server-jwt-keys \
  --from-file=private.key=./private.key \
  --from-file=public.key=./public.key

在Deployment中挂载Secret到容器路径:

spec:
  containers:
  - name: auth-server
    image: your-auth-server-image
    volumeMounts:
    - name: jwt-keys
      mountPath: /opt/auth/keys
      readOnly: true
  volumes:
  - name: jwt-keys
    secret:
      secretName: auth-server-jwt-keys

在配置文件中指定密钥路径:

auth.jwt.private-key-path=/opt/auth/keys/private.key
auth.jwt.public-key-path=/opt/auth/keys/public.key

2. 共享令牌授权存储

将默认的内存存储替换为JDBC存储,所有实例共享同一个数据库,确保令牌状态跨实例可访问。

添加JDBC版授权服务Bean:

// 新增以下两个Bean
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
    return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}

@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
    return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}

确保所有Auth Server实例连接同一个数据库(如K8s内部部署的MySQL/PostgreSQL),数据库连接配置统一。


3. 统一Issuer地址

将auth.server.issuer配置为Auth Server的负载均衡地址(而非单个实例地址),确保JWT的iss声明在所有实例中一致。

例如,使用K8s Service的内部域名:

auth.server.issuer=https://auth-service.default.svc.cluster.local

若对外提供服务,使用外部统一域名:

auth.server.issuer=https://auth.yourdomain.com

4. 验证配置有效性

  1. 启动两个Auth Server实例,检查所有实例加载的是同一套密钥对;
  2. 从任意实例获取令牌,调用任意实例的introspect端点,验证返回active: true;
  3. 确认资源服务器通过负载均衡地址调用introspect,而非直接访问单个实例。

内容的提问来源于stack exchange,提问作者kazuya komatsu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 10:28:19