oauth-2.0 使用Spring Webflux自定义JWT身份验证

iqih9akk  于 2022-10-31  发布在  Spring
关注(0)|答案(2)|浏览(362)

我正在尝试在Spring WebFlux应用程序中设置JWT与Spring Security的身份验证。我正在使用基于自定义JWT声明的自定义授权方案。我遇到的问题是,当我尝试调用安全端点时,Authentication failed: An Authentication object was not found in the SecurityContext身份验证失败。
下面是我使用的SecurityWebFilterChain

@Configuration
@EnableWebFluxSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfiguration {
    @Bean
    fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {

        return http {

            authorizeExchange {
                authorize(anyExchange, authenticated)
            }

            oauth2ResourceServer {
                jwt {
                    jwtAuthenticationConverter = grantedAuthoritiesExtractor()
                }
            }
        }
    }

    fun grantedAuthoritiesExtractor(): Converter<Jwt, Mono<AbstractAuthenticationToken>> {
        val jwtAuthenticationConverter = JwtAuthenticationConverter()
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(MappingJwtAuthoritiesConverter())
        //               custom JWT -> Collection<GrantedAuthority>  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

        return ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter)
    }

    @Bean
    fun jwtDecoder(): ReactiveJwtDecoder {
        val secretKey: SecretKey = SecretKeySpec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".toByteArray(), "HMACSHA256")

        return NimbusReactiveJwtDecoder.withSecretKey(secretKey).macAlgorithm(MacAlgorithm.HS256).build()
    }

    @Bean
    fun jwtValidator(): OAuth2TokenValidator<Jwt> {
        return OAuth2TokenValidator<Jwt> { OAuth2TokenValidatorResult.success() }
    }

    fun jwtAuthenticationManager(jwtDecoder: ReactiveJwtDecoder): JwtReactiveAuthenticationManager {
        return JwtReactiveAuthenticationManager(jwtDecoder).apply {
            this.setJwtAuthenticationConverter(grantedAuthoritiesExtractor())
        }
    }
}

下面是MappingJwtAuthoritiesConverter

class MappingJwtAuthoritiesConverter : Converter<Jwt, Collection<GrantedAuthority>> {
    companion object {
        private val WELL_KNOWN_CLAIMS: Set<String> = setOf("myCustomClaim")
    }

    override fun convert(jwt: Jwt): Collection<GrantedAuthority> {
        val authorities = jwt.claims.entries
            .filter { (key, _) -> key in WELL_KNOWN_CLAIMS }
            .map { (key, value) ->
                return@map SimpleGrantedAuthority("$key:$value")
            }

        return authorities
    }
}

我在网上搜索了一下,但是很多JWT/Spring Webflux实现都是手工进行JWT验证和处理的,我更愿意使用Spring在OAuth集成下已经提供的东西。现在我唯一使用的定制部分是从JWT到GrantedAuthority的转换器,但是我仍然不能让验证工作。
使用以下JWT:

header:
{
  "typ": "JWT",
  "alg": "HS256"
}

payload:
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1658474926,
  "exp": 1668478526,
  "myCustomClaim": "READ"
}

已编码:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjU4NDc0OTI2LCJleHAiOjE2Njg0Nzg1MjYsIm15Q3VzdG9tQ2xhaW0iOiJSRUFEIn0.lh66pHMd_xvXAF2itblHeHbZReJQA5xkGLKqXZV6MjI
我尝试保护的端点定义为:

@RestController
class FooController {
    @PreAuthorize("hasAuthority('myCustomClaim:READ')")
    @RequestMapping(
            method = [RequestMethod.GET],
            value = ["/foo"],
    )
    override suspend fun getFoo(): ResponseEntity<String> {
        return ResponseEntity.ok("Got foo")
    }
}

Spring日志:

2022-07-22 09:43:35.138 DEBUG 508191 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter    : [b7fe0c9e-1] HTTP GET "/foo"
2022-07-22 09:43:35.313 DEBUG 508191 --- [     parallel-2] o.s.w.s.s.DefaultWebSessionManager       : Created new WebSession.
2022-07-22 09:43:35.319 DEBUG 508191 --- [     parallel-2] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers$1@3602a026
2022-07-22 09:43:35.319 DEBUG 508191 --- [     parallel-2] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : matched
2022-07-22 09:43:35.319 DEBUG 508191 --- [     parallel-2] a.DelegatingReactiveAuthorizationManager : Checking authorization on '/foo' using org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager@2668fdab
2022-07-22 09:43:35.320 DEBUG 508191 --- [     parallel-2] o.s.s.w.s.a.AuthorizationWebFilter       : Authorization successful
2022-07-22 09:43:35.325 DEBUG 508191 --- [     parallel-2] s.w.r.r.m.a.RequestMappingHandlerMapping : [b7fe0c9e-1] Mapped to it.project.backend.controllers.FooController#getFoo(Continuation)
2022-07-22 09:43:35.665 DEBUG 508191 --- [     parallel-2] o.s.s.w.s.a.AuthenticationWebFilter      : Authentication failed: An Authentication object was not found in the SecurityContext
2022-07-22 09:43:35.694 DEBUG 508191 --- [     parallel-2] o.s.w.s.adapter.HttpWebHandlerAdapter    : [b7fe0c9e-1] Completed 401 UNAUTHORIZED

有什么想法吗?

ttcibm8c

ttcibm8c1#

问题是在配置bean中使用@EnableGlobalMethodSecurity而不是@EnableReactiveMethodSecurity。修复后,一切都开始工作。

3vpjnl9f

3vpjnl9f2#

看起来您构建jwtDecoder时使用的密钥与您发布的令牌的签名不对应,因此令牌验证失败。
您可以使用https://jwt.io/进行检查。

相关问题