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

OAuth2基于角色的身份认证配置后访问被拒绝问题求助

问题原因

你当前使用不透明令牌(Opaque Token)的默认配置时,Spring Security 只会从令牌内省结果的scope字段中拆分权限,且生成的权限会默认带上SCOPE_前缀,你存在其他字段中的用户角色/权限不会被自动解析为Spring Security识别的权限集合,因此hasAuthority校验不通过。

解决步骤

  1. 自定义令牌认证转换器,读取内省响应中你存储角色/权限的字段,手动转换为权限集合
  2. 将自定义转换器注册到Opaque Token配置中

完整配置示例

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Value("${oauth.enabled}")
    boolean oauthEnabled;

    @Value("${spring.security.oauth2.resourceserver.opaque.introspection-uri}")
    String introspectionUri;

    @Value("${spring.security.oauth2.resourceserver.opaque.introspection-client-id}")
    String clientId;

    @Value("${spring.security.oauth2.resourceserver.opaque.introspection-client-secret}")
    String clientSecret;

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        if (oauthEnabled) {
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and().cors()
                    .and()
                    .authorizeRequests(urlRegistry -> urlRegistry
                            .antMatchers("/api/health").permitAll()
                            .anyRequest().authenticated()
                    )
                    .oauth2ResourceServer(resourceServer -> resourceServer
                            .opaqueToken(opaqueToken -> opaqueToken
                                    .introspectionUri(this.introspectionUri)
                                    .introspectionClientCredentials(this.clientId, this.clientSecret)
                                    // 注册自定义转换器
                                    .authenticationConverter(customOpaqueTokenConverter())
                            )
                    );
        }
    }

    // 自定义不透明令牌权限转换器
    @Bean
    public OpaqueTokenAuthenticationConverter customOpaqueTokenConverter() {
        return (introspectedToken, principal) -> {
            // 先保留默认生成的scope权限
            List<GrantedAuthority> authorities = new ArrayList<>(principal.getAuthorities());
            // 从内省响应中读取角色字段,示例中字段为roles,根据你实际返回的字段名修改
            List<String> roles = introspectedToken.getClaimAsStringList("roles");
            if (roles != null) {
                roles.forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));
            }
            return new BearerTokenAuthentication(principal, introspectedToken, authorities);
        };
    }
}

验证方法

你可以在接口中打印当前用户的权限集合,确认是否已正确加载SOME_USER_ROLE:

@GetMapping("/icd")
@PreAuthorize("hasAuthority('SOME_USER_ROLE')")
public ResponseEntity<List<Countdown>> getIcd(@RequestParam(value = "val") String val) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    // 打印当前所有权限,排查是否存在匹配项
    System.out.println("当前用户权限:" + auth.getAuthorities());
    // 原有业务逻辑
}

注意事项

  • 请确保你的授权服务器返回的令牌内省响应中,确实包含存储用户角色的字段,且字段名和你代码中读取的名称一致
  • 如果你使用hasRole注解做校验,权限字符串需要带上ROLE_前缀,比如ROLE_SOME_USER_ROLE

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 16:45:02