Spring Security y @Async (usuarios autenticados confundidos)

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.

Sin embargo, 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, por supuesto, una excepción AccessDenied no AccessDenied . No hay tal problema con llamadas sincrónicas.

He definido , ¿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?

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 , 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.

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

      

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 siempre que llame a su método @Async, el subproceso de subprocesos usará el mismo contexto de seguridad que el subproceso de llamada.

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

           

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

 @Override public  Future submit(Callable task) { ExecutorService executor = getThreadPoolExecutor(); final Authentication a = SecurityContextHolder.getContext().getAuthentication(); try { return executor.submit(new Callable() { @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); } }