Spring Security 6资源服务器执行令牌introspect后OAuth2AuthenticatedPrincipal未加载问题
Spring Security 6资源服务器执行令牌introspect后OAuth2AuthenticatedPrincipal未加载问题
看起来你在Spring Security 6资源服务器里自定义不透明令牌解析器的时候,遇到了OAuth2AuthenticatedPrincipal加载异常的问题——大概率是权限提取环节出了疏漏,我来帮你梳理下可能的原因和解决办法。
首先看你贴的代码,CustomAuthoritiesOpaqueTokenIntrospector里的extractAuthorities方法只写了开头就中断了,这肯定会导致权限集合为空,进而让Principal看起来像“未加载”。另外,就算补全方法,也要结合授权服务器返回的introspect响应结构调整逻辑,不同授权服务器返回的权限字段可能不一样(比如有的用authorities,有的用scope或者自定义的permissions)。
第一步:补全并修复extractAuthorities方法
我给你一个通用的实现,覆盖了常见的授权服务器返回场景,你可以根据自己授权服务器的实际响应调整:
@Slf4j public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector { private final OpaqueTokenIntrospector delegate; // 把delegate改成final,避免后续被意外修改 public CustomAuthoritiesOpaqueTokenIntrospector(String oauthServerUrl, String clientId, String clientSecret) { this.delegate = new NimbusOpaqueTokenIntrospector(oauthServerUrl, clientId, clientSecret); } @Override public OAuth2AuthenticatedPrincipal introspect(String token) { OAuth2AuthenticatedPrincipal principal = this.delegate.introspect(token); // 打印Principal的所有属性,方便排查授权服务器返回的内容 log.debug("Introspected token attributes from auth server: {}", principal.getAttributes()); return new DefaultOAuth2AuthenticatedPrincipal( principal.getName(), principal.getAttributes(), extractAuthorities(principal)); } private Collection<GrantedAuthority> extractAuthorities(OAuth2AuthenticatedPrincipal principal) { Collection<GrantedAuthority> authorities = new ArrayList<>(); // 场景1:授权服务器返回的权限在"authorities"字段(List<String>类型) if (principal.getAttributes().containsKey("authorities")) { List<String> authStrings = principal.getAttribute("authorities"); authorities.addAll(authStrings.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList())); } // 场景2:授权服务器返回OIDC标准的scope字段(空格分隔的字符串) if (principal.getAttributes().containsKey("scope")) { String scopeString = principal.getAttribute("scope"); Arrays.stream(scopeString.split(" ")) // Spring Security要求scope权限必须带SCOPE_前缀 .map(scope -> new SimpleGrantedAuthority("SCOPE_" + scope)) .forEach(authorities::add); } // 场景3:自定义的permissions字段(根据你的授权服务器配置调整) if (principal.getAttributes().containsKey("permissions")) { List<String> permissions = principal.getAttribute("permissions"); authorities.addAll(permissions.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList())); } log.debug("Extracted final authorities: {}", authorities); return authorities; } }
第二步:确保自定义解析器被正确注册到Spring Security配置
很多人会犯的错是写了自定义Introspector,但没在资源服务器配置里指定,导致Spring用了默认实现。你需要在SecurityFilterChain里明确配置它:
@Configuration @EnableWebSecurity public class ResourceServerConfig { @Value("${oauth2.introspect-uri}") private String introspectUri; @Value("${oauth2.client-id}") private String clientId; @Value("${oauth2.client-secret}") private String clientSecret; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .opaqueToken(opaque -> opaque // 指定我们自定义的解析器 .introspector(new CustomAuthoritiesOpaqueTokenIntrospector(introspectUri, clientId, clientSecret)) ) ); return http.build(); } }
排查小技巧
- 先确认授权服务器的响应内容:用Postman或者curl调用授权服务器的introspect端点,传入你的令牌,看看返回的JSON里有哪些和权限相关的字段,比如是
authorities、scope还是其他自定义字段,这样才能精准提取。 - 加日志调试:在introspect方法里打印Principal的所有属性,确认授权服务器返回的内容有没有被正确解析到Principal的attributes里。
- 检查默认逻辑:如果你的授权服务器只是用标准的scope字段,其实Spring Security 6的默认NimbusOpaqueTokenIntrospector已经会自动解析成带SCOPE_前缀的权限,这时候你甚至不需要自定义解析器,除非要处理额外的权限字段。
备注:内容来源于stack exchange,提问作者Muratbek Bauyrzhan
相关产品推荐
相关产品推荐

