Webflux 和 keycloak 在 jwt 中使用方法安全级别@Preauthorize 自定义声明而不是默认范围

Webflux and keycloak using for method security level @Preauthorize custom claim in jwt instead of default scope

根据标题,我正在像这样设置 webflux 配置

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {
String jwkSetUri = "http://localhost:8080/auth/realms/demo/protocol/openid-connect/certs";
@Bean 
public ReactiveJwtDecoder jwtDecoder() {

return 
ReactiveJwtDecoders.fromIssuerLocation("http://localhost:8080/auth/realms/demo");
}
 @Bean

public SecurityWebFilterChain configure(ServerHttpSecurity http) {

     http.authorizeExchange()

           
            .oauth2Login()

            .and()
          .
            ...
             .oauth2ResourceServer()
             .jwt()

    .jwkSetUri(jwkSetUri)

            // .jwtDecoder(jwtDecoder())

             .jwtAuthenticationConverter(grantedAuthoritiesExtractor());
            return http.build();
}

@Bean

Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {

    GrantedAuthoritiesExtractor extractor = new GrantedAuthoritiesExtractor();

    return new ReactiveJwtAuthenticationConverterAdapter(extractor);

}
static class GrantedAuthoritiesExtractor

       extends  JwtAuthenticationConverter {


    public Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {

        Collection<?> authorities = (Collection<?>)

                jwt.getClaims().getOrDefault("roles", Collections.emptyList());


        return authorities.stream()

                .map(Object::toString)

                .map(SimpleGrantedAuthority::new)

                .collect(Collectors.toList());
    }
}

与以下application.yml

security:
   oauth2:

     client:
       provider:
         keycloak-local:
           issuer-uri: http://localhost:8080/auth/realms/demo
       registration:
         keycloak-local:
           client-id: some-id
           client-secret: ...

     resourceserver:
       jwt:
     
         issuer-uri: http://localhost:8080/auth/realms/demo
         jwk-set-uri: http://localhost:8080/auth/realms/demo/protocol/openid- 
         connect/certs

 

令牌格式就是这样

"realm_access": {
"roles": [
 "offline_access",
  "uma_authorization"
  ]
   },
 "resource_access": {
 "someclient": {
  "roles": [
    "uma_protection"
   ]
  },
  "account": {
    "roles": [
    "manage-account",
    "manage-account-links",
    "view-profile"
  ]
   }
   },
   "scope": "profile  email ",
   "organizationId": "605b74813bd72c65a24a1704",
   "accountId": "605b74813bd72c65a24a1709",
   "email_verified": false,
   "roles": [
   "ROLE_ADMIN"    
     ],
   "preferred_username": "someUsername",
   "userId": "605b74813bd72c65a24a1706",
   "email": "email.com"
    }`


    
  

当然,“角色”数组是我最想访问的数组(是我在我的数据库中定义的角色)。

设置之后似乎没有任何变化。 所以在我的控制器中我尝试使用 @Preauthorize 注释以访问某些方法,但它一直在使用范围声明。

@PreAuthorize("hasAuthority('SCOPE_email')")

有人知道我错过了什么吗?请注意,我参考了官方文档,但也参考了 this tutorial。 对于 webflux.

谢谢

默认情况下,Spring 安全会转换 scopescp 声明中的项目并使用 SCOPE_ 前缀。如果您的应用程序是纯 OAuth2 资源服务器,您可以通过定义自定义 ReactiveJwtAuthenticationConverter bean 来更改这两个约定。

例如,要从 roles 范围导出权限并使用 `` 前缀(因为 ROLE_ 已经是您的角色名称的一部分),您可以定义以下 bean。 Spring 安全性会自动选取它,您无需从 SecurityWebFilterChain 配置中引用它。

@Bean
public ReactiveJwtAuthenticationConverter jwtAuthenticationConverter() {
    var jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
    jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
    jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");

    var jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();
    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(
            new ReactiveJwtGrantedAuthoritiesConverterAdapter(jwtGrantedAuthoritiesConverter));

    return jwtAuthenticationConverter;
}

由于您的应用程序也配置为 OAuth2 客户端(使用 oauth2Login()),因此您需要一种不同的方法。您可以定义 GrantedAuthoritiesMapperOAuth2UserService。可以在 Spring Security documentation.

中找到这两个选项的示例