java - 如何将 PreAuthorize 与异步 Spring MVC Controller 一起使用

标签 java spring spring-mvc spring-security servlet-3.0

我有一个 Spring MVC 服务,可以让我上传文件。它使用来自 Spring Security 的 @PreAuthorize 来处理对资源的访问控制。 Controller 通过 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
        {
            ...
        }
    };
}

服务中的某处 - 在我的代码之外 - 抛出异常。

An Authentication object was not found in the SecurityContext

异常似乎是由 cglib 为我的 Spring Controller 生成的代码引发的。这是堆栈跟踪的大部分内容。

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]

我已经根据 this blog post 底部的说明更新了我的 web.xml关于 Spring Security 3.2 中的异步支持。

最后,错误并不总是会发生。它似乎只在我尝试上传大文件时发生。这让我认为它确实与尝试使 Controller 成为异步 Controller 有关。

版本:

  • Spring 3.2.4
  • Spring 安全 3.2
  • cglib 3.1
  • Java Servlet 3.0
  • Java 6
  • 8 号 jetty

最佳答案

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

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

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

SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL

或者在 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>

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

-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL

关于java - 如何将 PreAuthorize 与异步 Spring MVC Controller 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20863286/

相关文章:

java - 在测试方法中模拟 logger.debug() 调用

java - 在网络上共享对象(区 block 链)

java - OnSave 方法与 onload 方法一起使用吗?

java - 如何使用 spring boot 在 html 元标记中获取 csrf token

java - BeanPropertyRowMapper 在 spring 内部是如何工作的?

java - 在 Controller 方法中显式获取 post 参数或 query 参数

java - 在Java中,我需要读取一个文本文件并将每一行放入一个单独的数组中。但是每次我读取文本文件时我都无法分割行

Spring Boot 不提供静态内容

Spring JPA CrudRepository save(Entity) 在 id 字段中返回 0

java - 现场级反序列化不适用于 jackson