如何将PreAuthorize与异步Spring MVC控制器一起使用

Dav*_*d V 1 java spring spring-mvc spring-security servlet-3.0

我有一个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]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_65]
    at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_65]
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219) ~[spring-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132) ~[spring-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745) ~[spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686) ~[spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80) ~[spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) ~[spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) ~[spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936) [spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:849) [spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:758) [javax.servlet-3.0.0.v201112011016.jar:na]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812) [spring-webmvc-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:848) [javax.servlet-3.0.0.v201112011016.jar:na]
Run Code Online (Sandbox Code Playgroud)

我已经从本博文底部有关Spring Security 3.2中的异步支持的说明中更新了web.xml 。

最后,错误并不总是会发生。仅当我尝试上传大文件时,它才会出现。这使我认为它确实与尝试使控制器成为异步控制器有关。

版本:

  • 春天3.2.4
  • 春季安全3.2
  • cglib 3.1
  • Java Servlet 3.0
  • Java 6
  • 码头8

Wil*_*ing 5

如果上载发生在生成的线程中,则安全上下文可能不可用。这是因为默认情况下安全上下文绑定到父线程,而新的子线程将不知道。

您可以尝试更改SecurityContextHolder策略以InheritableThreadLocal使其可用于生成的线程。

您可以通过编程方式进行设置:

SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
Run Code Online (Sandbox Code Playgroud)

或在Spring XML配置中:

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass"
        value="org.springframework.security.core.context.SecurityContextHolder"/>
    <property name="targetMethod" value="setStrategyName"/>
    <property name="arguments"><list><value>MODE_INHERITABLETHREADLOCAL</value></list></property>
</bean>
Run Code Online (Sandbox Code Playgroud)

或者,您可以在启动应用程序时设置系统属性:

-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
Run Code Online (Sandbox Code Playgroud)