首页 > 解决方案 > JWT 令牌反应式身份验证出错

问题描述

我正在学习反应式编程和 spring-webflux,并且我正在尝试实现 JWT 令牌授权,但我收到有关 ResponseEntity 的运行时错误:

open class JwtReactiveAuthenticationManager(
        private val userDetailsService: ReactiveUserDetailsService,
        private val passwordEncoder: PasswordEncoder
): ReactiveAuthenticationManager {

    private val log: Logger = LoggerFactory.getLogger(this.javaClass)

    override fun authenticate(authentication: Authentication): Mono<Authentication> {
        return if (authentication.isAuthenticated) {
            Mono.just(authentication)
        } else Mono.just(authentication)
                .switchIfEmpty(Mono.error(BadCredentialsException("Invalid Credentials")))
                .cast(UsernamePasswordAuthenticationToken::class.java)
                .flatMap { authenticationToken: UsernamePasswordAuthenticationToken -> authenticateToken(authenticationToken) }
                .publishOn(Schedulers.parallel())
                .onErrorResume { Mono.error(BadCredentialsException("Invalid Credentials")) }
                .filter { u: UserDetails -> passwordEncoder.matches(authentication.credentials as String, u.password) }
                .switchIfEmpty(Mono.error(BadCredentialsException("Invalid Credentials")))
                .map { u: UserDetails -> UsernamePasswordAuthenticationToken(authentication.principal, authentication.credentials, u.authorities) }
    }

    private fun authenticateToken(authenticationToken: UsernamePasswordAuthenticationToken): Mono<UserDetails>? {
        val username = authenticationToken.name
        log.info("checking authentication for user $username")
        if (username != null && SecurityContextHolder.getContext().authentication == null) {
            log.info("authenticated user $username, setting security context")
            return this.userDetailsService.findByUsername(username)
        }
        return null
    }
}

调用的控制器方法:

@GetMapping("")
    override fun list(): Flux<ResponseEntity<Exercise>> {
        return repo.findAll().map { o -> ResponseEntity(o, HttpStatus.OK) }
    }
2020-01-09 17:54:15.790 ERROR 14680 --- [oundedElastic-1] a.w.r.e.AbstractErrorWebExceptionHandler : [e22e00ce]  500 Server Error for HTTP GET "/api/exercises"

java.lang.IllegalArgumentException: Only a single ResponseEntity supported
        at org.springframework.util.Assert.isTrue(Assert.java:118) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
        Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
        |_ checkpoint ? org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
        |_ checkpoint ? org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
        |_ checkpoint ? HTTP GET "/api/exercises" [ExceptionHandlingWebHandler]
Stack trace:
                at org.springframework.util.Assert.isTrue(Assert.java:118) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
                at org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler.handleResult(ResponseEntityResultHandler.java:121) ~[spring-webflux-5.2.2.RELEASE.jar:5.2.2.RELEASE]
                at org.springframework.web.reactive.DispatcherHandler.handleResult(DispatcherHandler.java:169) ~[spring-webflux-5.2.2.RELEASE.jar:5.2.2.RELEASE]
                at org.springframework.web.reactive.DispatcherHandler.lambda$handle$2(DispatcherHandler.java:147) ~[spring-webflux-5.2.2.RELEASE.jar:5.2.2.RELEASE]
                at org.springframework.web.reactive.DispatcherHandler$$Lambda$1353.000000001B373A70.apply(Unknown Source) ~[na:na]
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:118) ~[reactor-core-3.3.1.RELEASE.jar:3.3.1.RELEASE]
                at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1630) ~[reactor-core-3.3.1.RELEASE.jar:3.3.1.RELEASE]
                at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241) ~[reactor-core-3.3.1.RELEASE.jar:3.3.1.RELEASE]

标签: springreactive-programmingspring-webflux

解决方案


您不能Flux<ResponseEntity<Exercise>>像在多个响应实体中那样返回。

将其更改为:

@GetMapping("")
override fun list(): Flux<Exercise> {
    return repo.findAll()
}

如果您希望将数据流式传输到调用客户端,它可能会好很多。


推荐阅读