example ejemplo async spring asynchronous spring-security

ejemplo - spring boot rest asynchronous



Spring Security y @Async (5)

Invoco asincrónicamente el método con Spring, usando @ Async. Este método invoca otro método anotado con @PreAuthorize, Spring Security Annotation. Para hacer que la autorización funcione, tengo que configurar el modo SecurityContextHolder en MODE_INHERITABLETHREADLOCAL , de modo que la información de autenticación pase a la llamada asincrónica. Todo funciona bien hasta ahora.

Hovewer cuando cierro la sesión e inicio de sesión como un usuario diferente, en el método asincrónico SecurityContextHolder almacena la información de autenticación del usuario anterior, que se ha cerrado. Causa de una excepción AccessDenied no deseada de AccessDenied . No hay tal problema con llamadas sincrónicas.

He definido <task:executor id="executors" pool-size="10"/> , ¿puede ser un problema que una vez que se haya inicializado el hilo en el grupo de ejecutores, no anule la información de autenticación?

¡Gracias por la ayuda de antemano!

Saludos, Lukasz


Supongo que MODE_INHERITABLETHREADLOCAL no funciona correctamente con el grupo de subprocesos.

Como una posible solución puede intentar subclase ThreadPoolTaskExecutor y anular sus métodos para propagar SecurityContext manualmente, y luego declarar ese ejecutor en lugar de <task:executor> , algo como esto:

public void execute(final Runnable r) { final Authentication a = SecurityContextHolder.getContext().getAuthentication(); super.execute(new Runnable() { public void run() { try { SecurityContext ctx = SecurityContextHolder.createEmptyContext(); ctx.setAuthentication(a); SecurityContextHolder.setContext(ctx); r.run(); } finally { SecurityContextHolder.clearContext(); } } }); }


Esto es solo una pista que necesita una investigación futura (estoy demasiado cansado, pero tal vez alguien lo encuentre útil para futuras investigaciones):

Hoy tropecé con org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor vea GitHub .

parece que está diseñado para delegar el contexto de seguridad para que se "pase" a través de la llamada @Async .

También eche un vistazo a esta publicación: Spring Security 3.2 M1 Highlights, Servlet 3 API Support parece que está fuertemente relacionado.


Según la respuesta de @Ralph, se puede lograr el Aync event con Spring con threadpooling y delegar la seguridad usando http://docs.spring.io/autorepo/docs/spring-security/4.0.0.M1/apidocs/org/springframework/security /task/DelegatingSecurityContextAsyncTaskExecutor.html

Código de muestra

<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster"> <property name="taskExecutor"> <ref bean="delegateSecurityAsyncThreadPool"/> </property> </bean> <bean id="threadsPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> </bean> <bean id="delegateSecurityAsyncThreadPool" class="org.springframework.security.task.DelegatingSecurityContextTaskExecutor"> <constructor-arg ref="threadsPool"/> </bean>


Usando la información de Ralph y Oak -

Si desea que @Async funcione con la etiqueta ejecutor de tareas estándar, configuraría su configuración Spring XML como esta

<task:annotation-driven executor="_importPool"/> <task:executor id="_importPool" pool-size="5"/> <bean id="importPool" class="org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor"> <constructor-arg ref="_importPool"/> </bean>

Luego, en su método @Async, debe especificar el grupo que desea usar

@Async("importPool") public void run(ImportJob import) { ... }

Eso debería funcionar así cuando cada vez que llame a su método @Async, el subproceso de subprocesamiento utilizará el mismo contexto de seguridad que el subproceso de llamada.


Jus para agregar a la respuesta de @axtavt, también querrás anular otro método.

@Override public <T> Future<T> submit(Callable<T> task) { ExecutorService executor = getThreadPoolExecutor(); final Authentication a = SecurityContextHolder.getContext().getAuthentication(); try { return executor.submit(new Callable<T>() { @Override public T call() throws Exception { try { SecurityContext ctx = SecurityContextHolder.createEmptyContext(); ctx.setAuthentication(a); SecurityContextHolder.setContext(ctx); return task.call(); } catch (Exception e) { slf4jLogger.error("error invoking async thread. error details : {}", e); return null; } finally { SecurityContextHolder.clearContext(); } } }); } catch (RejectedExecutionException ex) { throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); } }