¿Puede explicar dónde el manejo de consejos @PreAuthorize("hasRole('ADMIN')") recupera el SecurityContext en una aplicación reactiva?

El siguiente ejemplo de Spring Security es una buena ilustración de este tipo de uso: https://github.com/spring-projects/spring-security/tree/5.0.0.M4/samples/javaconfig/hellowebflux-method

Después de verificar el código fuente de Spring Security Webflux, he encontrado algunas implementaciones de SecurityContextRepository pero el método de carga necesita el ServerWebExchange como parámetro.

Estoy tratando de entender cómo reemplazar la llamada SecurityContextHolder.getContext().getAuthentication() en un servicio estándar (porque ThreadLocal ya no es una opción en una Aplicación Reactiva), pero no entiendo cómo reemplazar esto con una llamada a un SecurityContextRepository sin una referencia en el ServerWebExchange.

14
etiennepeiniau 17 oct. 2017 a las 17:59

3 respuestas

La mejor respuesta

Tienes razón, ThreadLocal ya no es una opción porque el procesamiento de una solicitud no está vinculado a un hilo en particular.

Actualmente, Spring Security está almacenando la información de autenticación como un atributo ServerWebExchange, por lo que está vinculada al par actual de solicitud / respuesta. Pero aún necesita esa información cuando no tiene acceso directo al intercambio actual, como @PreAuthorize.

La información de autenticación se almacena en la tubería reactiva en sí (tan accesible desde su Mono o Flux), que es una característica de Reactor muy interesante: administrar un contexto vinculado a un Subscriber particular (en una aplicación web, el cliente HTTP extrae datos del servidor y actúa como tal).

No conozco un equivalente de SecurityContextHolder, o algún método de acceso directo para obtener la información de autenticación del contexto.

Consulte más información sobre Función de contexto de reactor en la documentación de referencia. También puede ver un ejemplo de que se está utilizando en Spring Security aquí.

10
Brian Clozel 17 oct. 2017 a las 16:25

El {{ X0}} proporciona la autenticación de manera reactiva, y es análogo a SecurityContextHolder.

Su getContext() proporciona un Mono<SecurityContext>, al igual que SecurityContextHolder.getContext() proporciona un SecurityContext.

ReactiveSecurityContextHolder
                    .getContext()
                    .map(context ->
                            context.getAuthentication()
10
Raedwald 22 mar. 2019 a las 12:50

Implementé un 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)
}

Y luego configuré un SecurityConfig como este:

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

http.addFilterAt( authFilter, SecurityWebFiltersOrder.AUTHENTICATION)

Puede utilizar este enfoque para personalizar su AuthenticationConverter como lo hice con la autenticación basada en jwt para establecer el objeto de autenticación deseado.

2
Tiago Peixoto 4 may. 2018 a las 14:52