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

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类型兼容:如果auth Claim是单个字符串而非列表,需替换为jwt.getClaimAsString("auth")并单独处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 01:12:19