Spring Security @PreAuthorize无法识别JWT中ROLE及转换器整合问题
解决OAuth2资源服务器同时校验Scope和自定义Role权限的问题
核心方案
通过自定义JwtAuthenticationConverter,结合DelegatingJwtGrantedAuthoritiesConverter同时处理默认的Scope权限和自定义auth Claim中的Role权限,实现两种权限的合并校验。
步骤1:实现自定义Role权限转换器
编写转换器提取JWT中auth Claim的角色信息,并添加ROLE_前缀(适配@PreAuthorize("hasRole('ADMIN')")的校验规则):
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; public class CustomJwtRoleAuthoritiesConverter extends JwtGrantedAuthoritiesConverter { @Override public Collection<GrantedAuthority> convert(Jwt jwt) { // 提取auth Claim中的角色列表 List<String> roles = jwt.getClaimAsStringList("auth"); if (roles == null || roles.isEmpty()) { return super.convert(jwt); } // 转换为带ROLE_前缀的权限 Collection<GrantedAuthority> roleAuthorities = roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); // 合并默认的Scope权限(如果不需要保留Scope可删除此句) roleAuthorities.addAll(super.convert(jwt)); return roleAuthorities; } }
步骤2:配置资源服务器的JWT转换器
通过DelegatingJwtGrantedAuthoritiesConverter组合默认Scope转换器和自定义Role转换器,再注入到JwtAuthenticationConverter中:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.server.resource.authentication.DelegatingJwtGrantedAuthoritiesConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import org.springframework.security.web.SecurityFilterChain; import java.util.List; @Configuration @EnableWebSecurity public class ResourceServerConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(jwtAuthenticationConverter()) ) ); return http.build(); } private JwtAuthenticationConverter jwtAuthenticationConverter() { JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); // 创建委托转换器,合并多个权限转换逻辑 DelegatingJwtGrantedAuthoritiesConverter delegatingConverter = new DelegatingJwtGrantedAuthoritiesConverter(); // 添加默认Scope转换器(处理SCOPE_前缀权限) JwtGrantedAuthoritiesConverter scopeConverter = new JwtGrantedAuthoritiesConverter(); // 添加自定义Role转换器(处理ROLE_前缀权限) CustomJwtRoleAuthoritiesConverter roleConverter = new CustomJwtRoleAuthoritiesConverter(); delegatingConverter.setAuthoritiesConverters(List.of(scopeConverter, roleConverter)); converter.setJwtGrantedAuthoritiesConverter(delegatingConverter); return converter; } }
关键说明
- 避免类型转换异常:不能直接将
DelegatingJwtGrantedAuthoritiesConverter设置为jwtAuthenticationConverter的返回值,必须通过JwtAuthenticationConverter的setJwtGrantedAuthoritiesConverter方法注入。 - 权限前缀适配:
hasRole()默认要求权限以ROLE_开头,若无需前缀可改用hasAuthority('ADMIN')。 - Claim类型兼容:如果
authClaim是单个字符串而非列表,需替换为jwt.getClaimAsString("auth")并单独处理。
内容的提问来源于stack exchange,提问作者m5c
相关产品推荐
相关产品推荐

