在 Spring Webflux Security 中使用多个 JWT 解码器
Posted
技术标签:
【中文标题】在 Spring Webflux Security 中使用多个 JWT 解码器【英文标题】:Using more than one JWT Decoder with Spring Webflux Security 【发布时间】:2021-10-11 18:19:30 【问题描述】:我阅读了 this post 关于在 Spring Security 流程中使用多个 JWT 解码器的文章,这似乎很容易,除了我使用的是 Spring Webflux 而不是 Spring WebMVC ,它有方便的WebSecurityConfigurerAdapter
,您可以扩展它以添加多个AuthenticationProvider
实例。使用 Webflux,您不再需要扩展某些类来配置安全性。
那么在尝试使用 Webflux 复制此内容时有什么问题? This 。正如您所读到的那样,Webflux 不使用 AuthenticationProvider
,您必须声明一个 ReactiveAuthenticationManager
。问题是我不知道如何让 Spring 使用多个身份验证管理器,每个身份验证管理器都使用自己的ReactiveJwtDecoder
。
我的第一个身份验证管理器将是 spring 使用此属性自动创建的一个:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: $scacap.auth0.issuer
我的第二个身份验证管理器将是我在安全@Configuration
中声明的自定义管理器:
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@EnableConfigurationProperties(JwkProperties::class)
internal class SecurityConfiguration
@Bean
fun securityFilter(
http: ServerHttpSecurity,
scalableAuthenticationManager: JwtReactiveAuthenticationManager
): SecurityWebFilterChain
http.csrf().disable()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer().jwt()
.jwtAuthenticationConverter(Auth0AuthenticationConverter())
return http.build()
@Bean
fun customAuthenticationManager(jwkProperties: JwkProperties): JwtReactiveAuthenticationManager
val decoder = NimbusReactiveJwtDecoder.withJwkSource Flux.fromIterable(jwkProperties.jwkSet.keys) .build()
return JwtReactiveAuthenticationManager(decoder).also
it.setJwtAuthenticationConverter(ScalableAuthenticationConverter())
我正在调试,似乎只选择了一个身份验证管理器,因此只能验证 auth0 令牌,但我也想用我自己的 JWKS 验证令牌
【问题讨论】:
要么在属性中指定所有配置,要么在自定义 HttpSecurity 配置中覆盖。您不能在属性中指定内容(使用 springs 自动配置),然后创建一个完整的自定义配置并期望两者都能正常工作。 @Toerktumlare 是真的。阅读您的评论后,我专注于使其与两个手动实例化的解码器一起工作。如果您对我是如何做到的感到好奇,您可以查看我的答案:) 【参考方案1】:好的,这就是我最终要做的:
我没有尝试将多个AuthenticationManager
s 传递给Spring Security 流,而是创建了一个包装器,我称之为DualAuthenticationManager
。这样对于 Spring,只有一个经理,我在包装器中进行编排,例如 firstManager.authenticate(auth).onErrorResume secondManager.authenticate(auth)
。
它最终比我想象的要短。这一切都在我的安全@Configuration
中的@Bean
函数中。每个经理都有自己的转换器功能,所以我可以使用两个不同的 JWT 创建我的 UserToken
模型:)
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@EnableConfigurationProperties(*[JwtProperties::class, Auth0Properties::class])
internal class SecurityConfiguration(
private val jwtProperties: JwtProperties,
private val auth0Properties: Auth0Properties
)
@Bean
fun securityFilter(
http: ServerHttpSecurity,
dualAuthManager: ReactiveAuthenticationManager
): SecurityWebFilterChain
http.csrf().disable()
.authorizeExchange()
.pathMatchers("/actuator/**").permitAll()
.pathMatchers("/user/**").hasAuthority(Authorities.USER)
.anyExchange().authenticated()
.and()
.oauth2ResourceServer().jwt()
.authenticationManager(dualAuthManager)
return http.build()
@Bean
fun dualAuthManager(): ReactiveAuthenticationManager
val firstManager = fromOidcIssuerLocation(auth0Properties.issuer).let decoder ->
JwtReactiveAuthenticationManager(decoder).also
it.setJwtAuthenticationConverter(FirstAuthenticationConverter())
val secondManager = withJwkSource fromIterable(jwtProperties.jwkSet.keys) .build().let decoder ->
JwtReactiveAuthenticationManager(decoder).also
it.setJwtAuthenticationConverter(SecondAuthenticationConverter())
return ReactiveAuthenticationManager auth ->
firstManager.authenticate(auth).onErrorResume secondManager.authenticate(auth)
这是我的转换器的外观:
class FirstAuthenticationConverter : Converter<Jwt, Mono<AbstractAuthenticationToken>>
override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken>
val authorities = jwt.getClaimAsStringList(AUTHORITIES) ?: emptyList()
val userId = jwt.getClaimAsString(PERSON_ID)
val email = jwt.getClaimAsString(EMAIL)
return Mono.just(
UsernamePasswordAuthenticationToken(
UserToken(jwt.tokenValue, UserTokenType.FIRST, userId, email),
null,
authorities.map SimpleGrantedAuthority(it)
)
)
然后在我的控制器中,我得到了我在转换器中构建的对象:
@AuthenticationPrincipal userToken: UserToken
【讨论】:
以上是关于在 Spring Webflux Security 中使用多个 JWT 解码器的主要内容,如果未能解决你的问题,请参考以下文章
在 Spring WebFlux 中使用 Spring Security 实现身份验证的资源是啥
如何在 Spring WebFlux Security(Reactive Spring Security)配置中将多个用户角色添加到单个 pathMatcher/Route?
在身份验证 Spring Security + WebFlux 期间抛出和处理自定义异常
Spring WebFlux + Security - 我们有“记住我”功能吗?
如何在 Spring webflux 应用程序中使用 Spring WebSessionIdResolver 和 Spring Security 5?