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 安全会转换 scope
或 scp
声明中的项目并使用 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()
),因此您需要一种不同的方法。您可以定义 GrantedAuthoritiesMapper
或 OAuth2UserService
。可以在 Spring Security documentation.
中找到这两个选项的示例
根据标题,我正在像这样设置 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 安全会转换 scope
或 scp
声明中的项目并使用 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()
),因此您需要一种不同的方法。您可以定义 GrantedAuthoritiesMapper
或 OAuth2UserService
。可以在 Spring Security documentation.