web-dev-qa-db-ja.com

@PreAuthorizeはリアクティブアプリケーションでどのように機能していますか、またはThreadLocalなしで生きる方法は?

@PreAuthorize("hasRole('ADMIN')")を処理するアドバイスがReactiveアプリケーションのSecurityContextを取得する場所を説明できますか?

次のSpringSecurityの例は、この種の使用法の良い例です。 https://github.com/spring-projects/spring-security/tree/5.0.0.M4/samples/javaconfig/hellowebflux-method

Spring Security Webfluxのソースコードを確認したところ、SecurityContextRepositoryの実装がいくつか見つかりましたが、loadメソッドにはパラメーターとしてServerWebExchangeが必要です。

標準サービスでSecurityContextHolder.getContext().getAuthentication()呼び出しを置き換える方法を理解しようとしています(ThreadLocalはリアクティブアプリケーションのオプションではなくなったため)が、これを置き換える方法がわかりませんSecurityContextRepositoryを参照せずにServerWebExchangeを呼び出します。

12
etiennepeiniau

確かに、リクエストの処理は特定のスレッドに関連付けられていないため、ThreadLocalはオプションではなくなりました。

現在、Spring Securityは認証情報をServerWebExchange属性として格納しているため、現在の要求/応答のペアに関連付けられています。ただし、@PreAuthorizeのように、現在の取引所に直接アクセスできない場合でも、その情報が必要です。

認証情報はReactiveパイプライン自体に保存されます(MonoまたはFluxからアクセスできます)。これは非常に興味深いReactor機能です。特定のSubscriberに関連付けられたコンテキストを管理します(Webアプリケーションでは、HTTPクライアントはからデータをプルしています。サーバーおよびそのように動作します)。

SecurityContextHolderに相当するもの、またはコンテキストから認証情報を取得するためのショートカットメソッドを知りません。

リファレンスドキュメントのReactor Context機能 の詳細を参照してください。 ここでSpring Securityで使用されている の例も見ることができます。

6
Brian Clozel

ReactiveSecurityContextHolder は、リアクティブな方法で認証を提供し、 SecurityContextHolder に類似しています。

その getContext() メソッドは _Mono<SecurityContext>_ を提供し、 SecurityContextHolder.getContext()SecurityContextを提供します。 。

_ReactiveSecurityContextHolder
                    .getContext()
                    .map(context ->
                            context.getAuthentication()
_
6

JwtAuthenticationConverter(kotlin)を実装しました:

@Component
class JwtAuthenticationConverter : Function<ServerWebExchange, 
Mono<Authentication>> {

@Autowired
lateinit var jwtTokenUtil: JwtTokenUtil

@Autowired
lateinit var userDetailsService: ReactiveUserDetailsService

private val log = LogFactory.getLog(this::class.Java)

override fun apply(exchange: ServerWebExchange): Mono<Authentication> {
    val request = exchange.request

    val token = getJwtFromRequest(request)

    if ( token != null )
        try {
            return userDetailsService.findByUsername(jwtTokenUtil.getUsernameFromToken(token))
                    .map { UsernamePasswordAuthenticationToken(it, null, it.authorities) }
        } catch ( e: Exception ) {
            exchange.response.statusCode = HttpStatus.UNAUTHORIZED
            exchange.response.headers["internal-message"] = e.message
            log.error(e)
        }

    return Mono.empty()
}

private fun getJwtFromRequest(request: ServerHttpRequest): String? {
    val bearerToken = request.headers[SecurityConstants.TOKEN_HEADER]?.first {
        it.startsWith(SecurityConstants.TOKEN_PREFIX, true)}
    return if (bearerToken.isNullOrBlank()) null else bearerToken?.substring(7, bearerToken.length)
}

次に、SecurityConfigを次のように設定します。

val authFilter = AuthenticationWebFilter(ReactiveAuthenticationManager {
    authentication: Authentication -> Mono.just(authentication)
})
authFilter.setAuthenticationConverter(jwtAuthenticationConverter)

http.addFilterAt( authFilter, SecurityWebFiltersOrder.AUTHENTICATION)

このアプローチを使用して、jwtベースの認証で行ったようにAuthenticationConverterをカスタマイズして、目的の認証オブジェクトを設定できます。

1
Tiago Peixoto