Webflux 和 keycloak 使用 jwt 中的方法安全级别 @Preauthorize 自定义声明而不是默认范围
Posted
技术标签:
【中文标题】Webflux 和 keycloak 使用 jwt 中的方法安全级别 @Preauthorize 自定义声明而不是默认范围【英文标题】:Webflux and keycloak using for method security level @Preauthorize custom claim in jwt instead of default scope 【发布时间】:2021-07-20 21:32:15 【问题描述】:根据标题,我正在像这样设置 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
token格式是这样的
"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。
谢谢
【问题讨论】:
【参考方案1】:默认情况下,Spring Security 转换 scope
或 scp
声明中的项目并使用 SCOPE_
前缀。如果您的应用程序是纯 OAuth2 资源服务器,您可以通过定义自定义 ReactiveJwtAuthenticationConverter
bean 来更改这两种约定。
例如,要从roles
范围导出权限并使用`` 前缀(因为ROLE_
已经是您角色名称的一部分),您可以定义以下bean。 Spring Security 会自动获取它,你不需要从你的 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 中找到这两个选项的示例。
【讨论】:
您好,Thomas 感谢您的回答。我试图摆脱 .jwtAuthenticationConverter(converter()) 的 SecurityWebFilterChain 并使用您建议的那个 bean。但是,@Preauthorize 仍在使用“范围”声明。我是否在 keycloak 方面遗漏了一些 jwtDecoder 或其他配置?请注意,角色数组类似于角色:[ROLE_ADMIN, ROLE_OTHER] 不是像范围声明那样的空格分隔值,但我不确定它是否适用。 我更新了我的答案(我之前的代码 sn-p 用于非反应性应用程序,现在应该是正确的)。你能再试一次吗? 仍然没有领域名称为空,只是更改了声明名称(请参阅我的编辑),但 @Preauthorize 仍然适用于范围声明。我错过了一些 jwtDecoder 吗? 我没有注意到您的应用程序同时配置为 OAuth2 客户端和 OAuth2 资源服务器,这是我的错误。 JwtAuthenticationConverter 方法仅适用于纯资源服务器。在您的情况下,您需要定义 GrantedAuthoritiesMapper 或 OAuth2UserService。我更新了我的答案以包含所有这些场景的信息。 我正在尝试离开资源服务器和 OAuth2 客户端(我也试图摆脱第一个客户端)并实现 Bean GrantedAuthoritiesMapper 但仍然没有(如果看起来有问题,请参阅 EDIT2)以上是关于Webflux 和 keycloak 使用 jwt 中的方法安全级别 @Preauthorize 自定义声明而不是默认范围的主要内容,如果未能解决你的问题,请参考以下文章
在 Spring Boot 中使用 Keycloak 实现 JWT、JWE 和 JWS(签名 JWT)
SpringSecurity - WebFlux环境下整合JWT使用 Token 认证授权
Kong + Keycloak + OAuth:jwt-keycloak 还是 oauth2 插件?