Spring Security - 使用RouterFunction,Principal始终为null

时间:2018-04-19 18:05:54

标签: spring spring-boot spring-security spring-webflux

我正在使用带有RouterFunction的Spring WebFlux来定义我的API端点。我通过以下方式配置Spring Security:

@Bean
fun security(httpSecurity: ServerHttpSecurity): SecurityWebFilterChain {
    return httpSecurity
            .authenticationManager(authenticationManager)
            // disable default security
                .httpBasic().disable()
                .formLogin().disable()
                .logout().disable()
                .csrf().disable()
            .addFilterAt(apiAuthenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
            .authorizeExchange()
                .pathMatchers("/app/health").permitAll()
                .pathMatchers("/app/info", "/app/loggers", "/app/metrics").hasAuthority(SecurityRole.SYSTEM)
                .anyExchange()
                .authenticated()
            .and()
                .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationFailureHandler)
            .and()
            .build()
}

@Bean
fun securityContextRepository(): NoOpServerSecurityContextRepository {
    return NoOpServerSecurityContextRepository.getInstance()
}

private fun apiAuthenticationWebFilter(): AuthenticationWebFilter {
    val apiAuthenticationWebFilter = AuthenticationWebFilter(authenticationManager)
    apiAuthenticationWebFilter.setAuthenticationFailureHandler(ServerAuthenticationEntryPointFailureHandler(jwtAuthenticationFailureHandler))
    apiAuthenticationWebFilter.setAuthenticationConverter(jwtAuthenticationConverter)
    apiAuthenticationWebFilter.setRequiresAuthenticationMatcher(PathPatternParserServerWebExchangeMatcher("/**"))
    apiAuthenticationWebFilter.setSecurityContextRepository(securityContextRepository())
    return apiAuthenticationWebFilter
}

RouterFunction声明如下:

@Bean
fun apiRouter(testHandler: TestHandler,
              errorHandler: ErrorHandler): RouterFunction<*> = router {
    (accept(MediaType.APPLICATION_JSON_UTF8).nest {
        GET(TEST_PATH, testHandler::test )
    })
}.andOther(route(RequestPredicates.all(), HandlerFunction { errorHandler.notFoundResponse() }))

处理程序本身:

@Component
class TestHandler(private val testService: TestService) {

    fun test(request: ServerRequest): Mono<ServerResponse> {
        return request.toMono()
                .transform({ testResponse() })
    }

问题是request.principal()始终为null与ReactiveSecurityContextHolder.getContext().block()结果相同,但身份验证配置正常工作并对用户进行身份验证。有趣的是,使用带注释的控制器主体声明端点是 not null 并显示正确认证的用户。

@RestController
class TestController(private val testService: TestService) {

    @GetMapping("/test")
    fun test(principal: Principal): Mono<String> {
        return testService.test().map { message -> message + " " + principal.name }
    }
}

是配置错误还是错误?有类似的问题,也许是同一个问题?

样品

回购 - https://github.com/yyunikov/spring-boot-2-kotlin-starter/

行 - https://github.com/yyunikov/spring-boot-2-kotlin-starter/blob/master/api/src/main/kotlin/com/yunikov/api/test/TestHandler.kt#L18

卷曲示例:

curl -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiJhNjQyMjk0NC01ZDFkLTQxODItOGE2ZS1mZGM0NjEwYzhlNTYiLCJzdWIiOiJ5dW5pa292IiwiaWF0IjoxNTIzNzQyNzcxLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvIiwiZW1haWwiOiJ1c2VyQHl1bmlrb3YuY29tIiwicm9sZXMiOlsidXNlciJdfQ.RirM-t7GZnSDQIAn_J-thD1UAzdyxKmiR4ktA_BdlYZsFoYKzy5nj-1dBaG60o7M9FcfwFLAWEe4pc7DplDNjw" localhost:8080/test

1 个答案:

答案 0 :(得分:1)

内部实现基于委托者,因此如果尚未解析,则返回null(此行为现在导致异常)

这是一个如何获得两者的例子。

fun secure(serverRequest: ServerRequest): Mono<ServerResponse> {
    return Mono.zip(
        serverRequest.principal(),
        ReactiveSecurityContextHolder.getContext()
    )
        .flatMap {
            val principal = it.t1 // Not null
            Assert.notNull(principal)
            val securityContext = it.t2 // Not null
            Assert.notNull(securityContext)
            ServerResponse.ok().body(fromObject("Hello $principal!"))
        }
}

请注意,使用block()/blockFirst()/blockLast()会导致IllegalStateException

出现异常