7

在 servlet 多部分发布期间,我无法访问 Spring Security 信息。Spring 安全信息在常规 get 和 post 方法中可用,但不适用于 multipart post 方法。我尝试通过 SecurityContextHolder.getContext().getAuthentication() 和通过访问 SecurityContextHolder.getContext().getAuthentication() 的注入服务直接访问此安全信息,但未成功。

我还实现了一个 HttpRequestHandler 和一个 ServletWrappingController。再一次,我能够成功地将 spring bean 注入它们并访问 Spring Security 信息以获取常规 get 和 post 方法,但我无法访问 Spring Security 信息以获取多部分帖子。我知道 Spring 3.0 中内置了新的 MultiPart 功能,但是因为我们的网站需要对文件上传流的完全访问权限,所以我将无法使用它们。出于这个原因,我将重点放在 HttpServlet、HttpRequestHandler 和 ServletWrappingController 上。

我在此处发布的代码是为解决我所面临的这个特定问题而编写的所有测试代码,因为在分段上传期间安全信息不可用(不意味着具有生产质量)。它适用于 HttpServlet。

如果我做错了什么,请告诉我。或者,如果没有,是否有解决方法或更好的方法来完成分段上传并访问 Spring Security 信息,同时保持对文件上传流的访问?有人可以为这个问题提供的任何帮助将不胜感激!

下面是测试 servlet 代码。下面的评论是基于使用 Spring Security 3.1 登录网站的用户,哪些有效,哪些无效:

//many import statements not displayed 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

public class UploadServlet extends HttpServlet {

    public void service(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
        super.service(req, res);
    }

    public void init(ServletConfig config) throws ServletException { 
        super.init(config); 

        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, 
          config.getServletContext()); 
    } 


    //The following is always injected and available
    //however, it only returns valid security information for regular get and post methods,
    //not for multipart post methods
    @Autowired 
    private CustomUserService customUserService; 

    //The following is always injected and available and always returns the expected data
    @Autowired 
    private GuideService guideService; 

    //the following does not work when the client issues a multipart post, it does work for non-multipart
    public boolean getAuthenticated(){
        boolean authorized = false;

        for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
            if(authority.getAuthority().equals("ROLE_USER") || authority.getAuthority().equals("ROLE_ADMIN")) {
                authorized = true;
                break;
            }  
        }

        return authorized;
    }


    //The following test get method works fine
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {           
        if(getAuthenticated()){
            PrintWriter out = resp.getWriter();
            out.write("<h1>Guide Info</h1><br/>");
            Guide guide = guideService.findById(2l);
            out.write(guide.getName() + "<br/>");
            out.write(guide.getDescription() + "<br/>");
            out.write("UserName: " + customUserService.getCurrentUser().getUsername() + "<br/>");
        }
        else{
            PrintWriter out = resp.getWriter();
            out.write("<h1>You're not authorized</h1><br/>");
        }
    }


    //This post method  
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //the following always works, whether the clients posts using multipart or not      
        String guideName = guideService.findById(2l).getName();

        //the following does not work when the client issues a multipart post, it does work for non-multipart
        String userName = customUserService.getCurrentUser().getUsername();

        //the following does not work when the client issues a multipart post, it does work for non-multipart
        if(getAuthenticated()){
            String responseString = RESP_SUCCESS;
            boolean isMultipart = ServletFileUpload.isMultipartContent(req);

            if (isMultipart) {
                ServletFileUpload upload = new ServletFileUpload();

                //commmons fileupload code

            // Not a multi-part MIME request.
            else {
                //...
            }
            //...
        }
        else{
            //...
        }


    }

}

这是 web.xml 的相关部分:

<servlet>
    <servlet-name>fgm</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/spring/webmvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup> 
</servlet>

<servlet-mapping>
    <servlet-name>fgm</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>com.guides.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/upload</url-pattern>
</servlet-mapping>
4

4 回答 4

2

我可以确认 Spring 3.0.x 和 Spring Security 3.0.x 可以与多部分帖子一起使用,也可以与其他类型的请求一起使用。我遇到了类似的行为,在我们的例子中,由于我们在过滤器映射中的错误,安全过滤器没有应用于请求。

您可以发布定义安全过滤器的 web.xml 部分,并将其映射到所需的路径吗?

于 2012-06-04T17:06:34.673 回答
0

如果您使用的是 Spring MVC,这可能会对您有所帮助:

{
  @RequestMapping(method = RequestMethod.POST, value = "/some/post/url")
  public void postFile(MultipartHttpServletRequest request) {
    MultipartFile multipartFile = request.getFileMap().get("fileControlName");
    ...
  }
}
于 2012-05-25T08:26:30.070 回答
0

SecurityContextHolder 提供的安全详细信息(默认情况下)存储在 ThreadLocal 中。

上传 servlet 是否会创建一个新线程来处理多部分?尝试将 SecurityContextHolderStrategy 更改为 MODE_INHERITABLETHREADLOCAL

类似问题:如何设置 Spring Security SecurityContextHolder 策略?

于 2012-05-29T07:08:37.353 回答
0

可能值得检查您的客户如何执行多部分帖子,您是否使用与标准帖子不同的机制/库?

如果我不得不猜测,我会说您的客户端代码没有正确验证多部分用例。

例如,将标准 Java 用于普通帖子,将 Apache 库用于多部分帖子,而在使用 Apache 的东西时忘记设置适当的 http 标头。

于 2012-05-29T07:51:19.480 回答