基于JWT载荷角色权限访问REST接口未授权问题排查
问题分析与解决方案
核心问题
当前配置下持续收到未授权错误,根源集中在以下三点:
1. 角色完全不匹配
安全配置要求访问/api/**的用户必须拥有User1、Admin3或TestDataUploader角色,但提供的JWT Token中实际仅包含Admin、Approve角色,两者无交集,这是直接触发未授权的核心原因。
2. Spring Security角色前缀不兼容
hasAnyRole()方法会自动为传入的角色名添加ROLE_前缀进行匹配,但你的JwtGrantedAuthoritiesConverter设置了authorityPrefix(""),导致从JWT解析出的权限是Admin、Approve(不带前缀),与hasAnyRole预期的ROLE_Admin格式不匹配。
3. 冗余权限Mapper无效且干扰
你定义的userAuthoritiesMapper是针对OIDC用户(OidcUserAuthority)的逻辑,但当前是纯JWT资源服务器场景,该Bean不会触发,反而可能导致权限处理混乱,日志也无法正确输出JWT解析出的权限。
修复步骤
步骤1:修正角色匹配逻辑
二选一调整,确保配置角色与JWT实际角色一致:
- 方式A:修改安全配置,允许JWT中存在的角色访问:
.requestMatchers(antMatcher("/api/**")) .hasAnyRole("Admin", "Approve") - 方式B:确保JWT的
role声明中包含User1、Admin3或TestDataUploader中至少一个角色。
步骤2:解决前缀兼容问题
针对hasAnyRole()的前缀问题,两种处理方式:
方式A:给JWT权限添加ROLE_前缀
修改jwtAuthenticationConverter()方法,设置正确前缀:
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { var grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); grantedAuthoritiesConverter.setAuthoritiesClaimName("role"); grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); // 添加ROLE_前缀适配hasAnyRole var jwtAuthenticationConverter = new JwtAuthenticationConverter(); jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); return jwtAuthenticationConverter; }
方式B:改用hasAnyAuthority()替代hasAnyRole()
直接匹配不带前缀的权限,无需修改转换器:
.requestMatchers(antMatcher("/api/**")) .hasAnyAuthority("Admin", "Approve") // 替换hasAnyRole为hasAnyAuthority
步骤3:移除冗余权限Mapper
删除userAuthoritiesMapper() Bean,当前JWT资源服务器场景下该Bean无效,只会增加复杂度。
完整修正后的安全配置类
@Configuration @EnableWebSecurity(debug = true) @AllArgsConstructor public class SecurityOAuth2Config { private static final Logger LOGGER = LogManager.getLogger(SecurityOAuth2Config.class); @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors(AbstractHttpConfigurer::disable) .headers(header -> header.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(req -> req .requestMatchers(antMatcher("/actuator/health")).permitAll() .requestMatchers(antMatcher("/actuator/info")).permitAll() .requestMatchers(antMatcher("/h2-console/**")).permitAll() .requestMatchers(antMatcher("/internal/api/**")).permitAll() .requestMatchers(antMatcher("/api/**")) .hasAnyAuthority("Admin", "Approve") // 使用hasAnyAuthority匹配实际角色 .anyRequest().permitAll() ) .oauth2ResourceServer((oauth2) -> oauth2.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(jwtAuthenticationConverter()))); return http.getOrBuild(); } @Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { var grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); grantedAuthoritiesConverter.setAuthoritiesClaimName("role"); grantedAuthoritiesConverter.setAuthorityPrefix(""); // 保持前缀为空,配合hasAnyAuthority使用 var jwtAuthenticationConverter = new JwtAuthenticationConverter(); jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); return jwtAuthenticationConverter; } }
验证建议
- 开启
@EnableWebSecurity(debug = true)后,查看控制台输出的权限匹配日志,确认JWT解析出的权限是否正确,以及/api/app接口的权限检查逻辑。 - 在控制器中添加权限打印代码,验证解析结果:
@GetMapping("/app") public Collection<Response> getApplications(Authentication authentication) { LOGGER.info("当前用户权限:{}", authentication.getAuthorities()); return serviceApp.loadResponse(); }
内容的提问来源于stack exchange,提问作者cUser
相关产品推荐
相关产品推荐

