如何使用 Spring Security 很好地处理文件上传 MaxUploadSizeExceededException [英] How to nicely handle file upload MaxUploadSizeExceededException with Spring Security

查看:48
本文介绍了如何使用 Spring Security 很好地处理文件上传 MaxUploadSizeExceededException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 Spring Web 4.0.5、Spring Security 3.2.4、Commons FileUpload 1.3.1、Tomcat 7,当我的上传大小超过限制时,我得到了一个丑陋的 MaxUploadSizeExceededException,这会导致500 内部服务器错误".我用一个很好的通用弹出窗口来处理它,但我宁愿让我的控制器通过使用正确的解释消息返回到原始表单来处理它.

I'm using Spring Web 4.0.5, Spring Security 3.2.4, Commons FileUpload 1.3.1, Tomcat 7 and I'm getting an ugly MaxUploadSizeExceededException when my upload size limit is exceeded, which results in a "500 Internal Server Error". I handle it with a nice generic popup, but I'd rather have my Controller take care of it by going back to the originating form with the proper explanation message.

我多次看到有人问过类似的问题,其中提供了一些在不使用 Spring Security 时可能有效的解决方案;我试过的所有方法都不适合我.

I've seen a similar question asked many times, with a few solutions that might work when not using Spring Security; none of the ones I tried worked for me.

问题可能是在使用 Spring Security 时,CommonsMultipartResolver 不是作为multipartResolver"bean 添加的,而是作为filterMultipartResolver"添加的:

The problem might be that when using Spring Security, the CommonsMultipartResolver is not added as a "multipartResolver" bean but as a "filterMultipartResolver":

@Bean(name="filterMultipartResolver")
CommonsMultipartResolver filterMultipartResolver() {
    CommonsMultipartResolver filterMultipartResolver = new CommonsMultipartResolver();
    filterMultipartResolver.setMaxUploadSize(MAXSIZE);
    return filterMultipartResolver;
}

如果我设置了 filterMultipartResolver.setResolveLazily(true); 就没什么区别了.

If I set filterMultipartResolver.setResolveLazily(true); it makes no difference.

如果我用我自己的子类化 CommonsMultipartResolver 并用捕获 MaxUploadSizeExceededException 的东西覆盖 parseRequest() 方法并返回一个空的 MultipartParsingResult,我收到403 Forbidden"错误:

If I subclass the CommonsMultipartResolver with my own and override the parseRequest() method with something that traps the MaxUploadSizeExceededException and returns an empty MultipartParsingResult, I get a "403 Forbidden" error:

public class ExtendedCommonsMultipartResolver extends CommonsMultipartResolver {
    protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
        String encoding = determineEncoding(request);
        try {
            return super.parseRequest(request);
        } catch (MaxUploadSizeExceededException e) {
            return parseFileItems(Collections.<FileItem> emptyList(), encoding);
        }
    }
}

最后,实现某种本地或全局 ExceptionHandler 是没有意义的,因为它永远不会被调用.

Finally, there's no point in implementing some kind of local or global ExceptionHandler because it is never called.

如果我找不到更好的解决方案,我将删除上传大小限制并在控制器中自己处理,缺点是让用户等到上传完成后才能看到有关文件的错误消息尺寸.我什至可能会忽略所有这些,因为在这种情况下,它是一个图像,我可以将其调整为适当的值.

If I don't find a better solution, I'll just remove the upload size limit and handle it myself in the controller, with the drawback of having the user wait until the upload is finished before seeing the error message about file size. Of I might even ignore all of this because, being it an image in this case, I could just resize it down to proper values.

不过,我想看看这个问题的解决方案.

Still, I'd like to see a solution to this problem.

谢谢

我按照要求添加了堆栈跟踪.这是生成 500 的情况.

I add the stack trace as requested. This is the case where a 500 is generated.

May 30, 2014 12:47:17 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/site] threw exception
org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size of 1000000 bytes exceeded; nested exception is org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (3403852) exceeds the configured maximum (1000000)
    at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:162)
    at org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:142)
    at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:110)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:409)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1044)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
Caused by: org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (3403852) exceeds the configured maximum (1000000)
    at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:965)
    at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:310)
    at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:334)
    at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:115)
    at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:158)
    ... 19 more

推荐答案

您可以通过添加额外的过滤器来处理 MaxUploadSizeExceededException 以捕获异常并重定向到错误页面.例如,您可以创建一个 MultipartExceptionHandler 过滤器,如下所示:

You can handle the MaxUploadSizeExceededException by adding an additional Filter to catch the exception and the redirect to an error page. For example, you could create a MultipartExceptionHandler Filter like the following:

public class MultipartExceptionHandler extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } catch (MaxUploadSizeExceededException e) {
            handle(request, response, e);
        } catch (ServletException e) {
            if(e.getRootCause() instanceof MaxUploadSizeExceededException) {
                handle(request, response, (MaxUploadSizeExceededException) e.getRootCause());
            } else {
                throw e;
            }
        }
    }

    private void handle(HttpServletRequest request,
            HttpServletResponse response, MaxUploadSizeExceededException e) throws ServletException, IOException {

        String redirect = UrlUtils.buildFullRequestUrl(request) + "?error";
        response.sendRedirect(redirect);
    }

}

注意:此重定向假设您的表单和上传.您可能需要修改重定向到的位置.具体来说,如果您遵循 GET 表单的模式并在 POST 处理它,这将起作用.

NOTE: This redirect makes an assumption about your form and upload. You may need to modify where to redirect to. Specifically if you follow the pattern of your form being at GET and it is processed at POST this will work.

然后您可以确保在 MultipartFilter 之前添加此过滤器.例如,如果您使用 web.xml,您将看到如下内容:

You can then ensure to add this Filter before MultipartFilter. For example, if you are using web.xml you would see something like this:

<filter>
    <filter-name>meh</filter-name>
    <filter-class>org.example.web.MultipartExceptionHandler</filter-class>
</filter>
<filter>
    <description>
        Allows the application to accept multipart file data.
    </description>
    <display-name>springMultipartFilter</display-name>
    <filter-name>springMultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
    <!--init-param>
        <param-name>multipartResolverBeanName</param-name>
        <param-value>multipartResolver</param-value>
    </init-param-->
</filter>
<filter>
    <description>
        Secures access to web resources using the Spring Security framework.
    </description>
    <display-name>springSecurityFilterChain</display-name>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>meh</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>springMultipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

在您的表单中,您可以通过检查是否存在 HTTP 参数错误来检测是否发生错误.例如,在 JSP 中,您可以执行以下操作:

In your form you can then detect if the error occurred by inspecting if the HTTP parameter error is present. For example, in a JSP you might do the following:

<c:if test="${param.error != null}">
    <p>Failed to upload...too big</p>
</c:if>

PS:我创建了 SEC-2614 来更新文档以讨论错误处理

PS: I created SEC-2614 to update the documentation to discuss error handling

这篇关于如何使用 Spring Security 很好地处理文件上传 MaxUploadSizeExceededException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆