我在我的服务中使用异步方法(Spring 3 @Async注释).而且我遇到了问题 - 衍生线程没有安全上下文.原因是Spring Security默认使用SecurityContextHolder.MODE_THREADLOCAL其上下文持有者的策略.但我需要使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL策略.目前我在AuthenticationSuccessHandler中设置了策略.但在我看来,这不是一个好习惯.
那么如何在上下文配置文件中进行设置呢?
spring security的版本是3.0.0.
我正在尝试从应用程序spring的spring上下文中获取用户,如下所示:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Run Code Online (Sandbox Code Playgroud)
问题在于方法是异步的,带有注释@Async:
@Service
@Transactional
public class FooServiceImpl implements FooService {
@Async("asyncExecutor")
public void fooMethod(String bar) {
System.out.println("Foo: " + bar);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
}
}
Run Code Online (Sandbox Code Playgroud)
问题在于异步方法在另一个上下文中的另一个线程中运行。我尝试使用SecurityContextDelegationAsyncTaskExecutor。用户将传播到异步方法,但是如果我注销,则异步方法中的用户为null。这是我的代码:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(1);
executor.setThreadGroupName("MyCustomExecutor");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setBeanName("asyncExecutor");
executor.initialize();
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
Run Code Online (Sandbox Code Playgroud)
我还使用了ThreadPoolTaskExecutor并通过“ MODE_INHERITABLETHREADLOCAL”设置了弹簧安全性的上下文。但是结果是一样的。如果我登录到应用程序,则用户不为null。如果未登录,则用户为null。我真的希望运行该方法的用户,而不是当前登录的用户。我的代码:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer …Run Code Online (Sandbox Code Playgroud) 这是我的情景:
我的应用程序启用了Mongo审核,使用自定义AuditorAware从当前用户获取SecurityContext.这适用于同步方法,并且当前审计员已成功保存,但我无法使其与@Async方法一起正常工作.
我有一个异步方法(CompletableFuture),可以对我的Mongo数据库进行一些更新.当AuditorAware.getCurrentAuditor()被调用时,没有任何身份验证信息存在,我不能让现任核数师(SecurityContextHolder.getContext().getAuthentication()回报null).
@Override
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()
|| authentication instanceof AnonymousAuthenticationToken) {
log.error("Not authenticated");
return null;
}
[...]
}
Run Code Online (Sandbox Code Playgroud)
我用的是DelegatingSecurityContextAsyncTaskExecutor:
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(200);
executor.initialize();
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new ItacaExceptionHandler();
} …Run Code Online (Sandbox Code Playgroud) 我将 Async 与使用 Feign 调用远程服务的方法一起使用,我需要将 oauth2 令牌附加到请求中,为此我使用了 RequestInterceptor。
@Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return requestTemplate -> {
Object principal = SecurityContextHolder
.getContext()
.getAuthentication()
.getPrincipal();
if (!principal.equals("anonymousUser")) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
SecurityContextHolder.getContext().getAuthentication().getDetails();
requestTemplate.header("Authorization", "bearer " + details.getTokenValue());
}
};
}
Run Code Online (Sandbox Code Playgroud)
但是当 requestInterceptor 在另一个线程中使用时,我无法访问相同的安全上下文,因此 getAuthentication 返回 null。
我尝试在执行程序配置中修复它,我创建了一个 DelegatingSecurityContextExecutor 来包装执行程序和安全上下文。但似乎 bean 是在“主”线程中创建的,并且安全上下文与当时使用的安全上下文不同,当执行 RestController 方法时,因此 getAuthentication() 仍然返回 null。
@Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsynchThread-");
executor.initialize();
Executor wrappedExecutor = new DelegatingSecurityContextExecutor(executor, SecurityContextHolder.getContext());
return …Run Code Online (Sandbox Code Playgroud) multithreading asynchronous spring-mvc spring-boot spring-cloud-feign
我有一个Spring MVC服务,可以让我上传文件。它使用@PreAuthorizeSpring Security来处理对资源的访问控制。控制器通过使用Callable来使用Servlet 3异步servlet。
@PreAuthorize("...")
@RequestMapping(value = "upload", method = RequestMethod.PUT)
public Callable<ResponseEntity> upload(final InputStream inputStream)
{
return new Callable<ResponseEntity>()
{
@Override
public ResponseEntity call() throws Exception
{
...
}
};
}
Run Code Online (Sandbox Code Playgroud)
服务中的某个位置-我的代码之外-引发了异常。
An Authentication object was not found in the SecurityContext
Run Code Online (Sandbox Code Playgroud)
该异常似乎是由cglib为我的Spring控制器生成的代码引发的。这是大部分堆栈跟踪。
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:339) ~[spring-security-core-3.2.0.RELEASE.jar:3.2.0.RELEASE]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:198) ~[spring-security-core-3.2.0.RELEASE.jar:3.2.0.RELEASE]
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60) ~[spring-security-core-3.2.0.RELEASE.jar:3.2.0.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631) ~[spring-aop-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at com.testing.upload.controller.RESTService$$EnhancerByCGLIB$$66a0c4c9.upload(<generated>) ~[spring-core-3.2.4.RELEASE.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_65]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_65] …Run Code Online (Sandbox Code Playgroud)