如何在Spring Authorization Server中无密码重签带新增声明的JWT
问题根因
原有代码无法生成新令牌的核心原因:
JwtAuthenticationProvider的唯一职责是校验传入Bearer Token的有效性、解析令牌内容构建认证对象,本身不承载任何新令牌签发逻辑,传入现有有效JWT走该流程只会返回解析后的原令牌认证信息,自然不会产出新令牌。- Spring Authorization Server(以下简称SAS)的令牌签发逻辑默认和授权授予(Authorization Grant)流程绑定,没有直接暴露"传入旧Token直接签发新Token"的开箱即用接口,需要手动复用官方核心服务组件实现自定义逻辑。
实现方案(无用户交互、无需Refresh Token)
核心思路是复用SAS内置的OAuth2TokenGenerator、OAuth2AuthorizationService等核心组件,基于当前已认证的用户上下文直接生成带新声明的JWT,全程不需要用户交互,也不依赖Refresh Token机制。
第一步:配置自定义声明注入逻辑
实现OAuth2TokenCustomizer<JwtEncodingContext>,仅在令牌重签场景下注入你需要的声明集合B,避免影响正常登录等其他流程的令牌生成:
@Bean public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() { return context -> { // 匹配自定义的令牌重签授权类型 if ("urn:ietf:params:oauth:grant-type:token-renew".equals(context.getAuthorizationGrantType().getValue())) { // 此处写入声明集合B,可从当前认证上下文、业务数据库查询对应值写入 context.getClaims().claim("customField1", "value1"); context.getClaims().claim("customField2", "value2"); } }; }
第二步:编写令牌重签接口
注入SAS核心组件,构造合法的令牌生成上下文,直接调用令牌生成器产出新JWT,同时将新令牌的授权信息持久化到SAS的授权存储中,和官方原生流程保持一致:
@RestController public class TokenRenewController { private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator; private final OAuth2AuthorizationService authorizationService; private final RegisteredClientRepository registeredClientRepository; // 构造注入SAS核心Bean public TokenRenewController(OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator, OAuth2AuthorizationService authorizationService, RegisteredClientRepository registeredClientRepository) { this.tokenGenerator = tokenGenerator; this.authorizationService = authorizationService; this.registeredClientRepository = registeredClientRepository; } @PostMapping("/renew") public OAuth2AccessTokenResponse renewToken() { // 从SecurityContext获取当前已认证的用户身份 Authentication currentUserAuth = SecurityContextHolder.getContext().getAuthentication(); // 替换为你当前公开客户端在SAS中注册的clientId String clientId = "your-public-client-id"; RegisteredClient registeredClient = registeredClientRepository.findByClientId(clientId); // 定义自定义的令牌重签授权类型 AuthorizationGrantType renewGrantType = new AuthorizationGrantType("urn:ietf:params:oauth:grant-type:token-renew"); Set<String> authorizedScopes = new HashSet<>(registeredClient.getScopes()); // 构造令牌生成上下文 OAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder() .registeredClient(registeredClient) .principal(currentUserAuth) .authorizationService(authorizationService) .authorizedScopes(authorizedScopes) .tokenType(OAuth2TokenType.ACCESS_TOKEN) .authorizationGrantType(renewGrantType) .build(); // 生成带新声明的JWT OAuth2Token generatedToken = tokenGenerator.generate(tokenContext); if (generatedToken == null) { throw new IllegalStateException("新令牌生成失败"); } Jwt newJwt = (Jwt) generatedToken; // 将新令牌对应的授权信息存入SAS授权存储,保证后续校验正常 OAuth2Authorization newAuthorization = OAuth2Authorization.withRegisteredClient(registeredClient) .principalName(currentUserAuth.getName()) .authorizationGrantType(renewGrantType) .authorizedScopes(authorizedScopes) .token(newJwt, metadata -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, newJwt.getClaims()) ) .build(); authorizationService.save(newAuthorization); // 按标准OAuth2令牌格式返回 return OAuth2AccessTokenResponse.withToken(newJwt.getTokenValue()) .tokenType(OAuth2AccessToken.TokenType.BEARER) .expiresIn(newJwt.getExpiresAt().getEpochSecond() - Instant.now().getEpochSecond()) .scopes(authorizedScopes) .build(); } }
第三步:配置接口访问规则
将重签接口加入SAS安全匹配路径,要求必须携带有效JWT才能访问,避免匿名调用:
@Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfigurer sasConfigurer = new OAuth2AuthorizationServerConfigurer(); http.securityMatcher(endpoints -> { List<RequestMatcher> matchers = new ArrayList<>(); matchers.add(new AntPathRequestMatcher("/renew", HttpMethod.POST.name())); matchers.addAll(sasConfigurer.getEndpointsMatcher().getMatchers()); return new OrRequestMatcher(matchers).matches(endpoints); }) .authorizeHttpRequests(auth -> auth .requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/renew")).authenticated() .anyRequest().authenticated() ) // 开启JWT资源服务器校验,保证接口必须携带有效JWT才能访问 .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); // 原有SAS配置保持不变即可 http.apply(sasConfigurer); return http.build(); }
注意事项
- 若需要让旧令牌在新令牌签发后立即失效,直接调用
authorizationService.remove()删除旧JWT对应的OAuth2Authorization记录即可,不需要额外实现黑名单逻辑,SAS本身会基于持久化的授权记录校验令牌有效性。 - 上述实现完全不涉及Refresh Token签发逻辑,适配公开客户端的权限限制,不需要修改原有客户端的配置属性。
- 自定义授权类型标识可自行命名,只要不与SAS内置的
authorization_code、client_credentials、refresh_token等内置类型冲突即可,不会影响原有认证流程。 - 新JWT默认沿用对应
RegisteredClient配置的Access Token有效期,若需要自定义重签令牌的TTL,可在构造DefaultOAuth2TokenContext时通过tokenSettings传入自定义有效期参数。
内容的提问来源于stack exchange,提问作者Ahmad Jahanbin
相关产品推荐
相关产品推荐

