@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
を呼び出します。
確かに、リクエストの処理は特定のスレッドに関連付けられていないため、ThreadLocal
はオプションではなくなりました。
現在、Spring Securityは認証情報をServerWebExchange
属性として格納しているため、現在の要求/応答のペアに関連付けられています。ただし、@PreAuthorize
のように、現在の取引所に直接アクセスできない場合でも、その情報が必要です。
認証情報はReactiveパイプライン自体に保存されます(Mono
またはFlux
からアクセスできます)。これは非常に興味深いReactor機能です。特定のSubscriber
に関連付けられたコンテキストを管理します(Webアプリケーションでは、HTTPクライアントはからデータをプルしています。サーバーおよびそのように動作します)。
SecurityContextHolder
に相当するもの、またはコンテキストから認証情報を取得するためのショートカットメソッドを知りません。
リファレンスドキュメントのReactor Context機能 の詳細を参照してください。 ここでSpring Securityで使用されている の例も見ることができます。
ReactiveSecurityContextHolder
は、リアクティブな方法で認証を提供し、 SecurityContextHolder
に類似しています。
その getContext()
メソッドは _Mono<SecurityContext>
_ を提供し、 SecurityContextHolder.getContext()
はSecurityContext
を提供します。 。
_ReactiveSecurityContextHolder
.getContext()
.map(context ->
context.getAuthentication()
_
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をカスタマイズして、目的の認証オブジェクトを設定できます。