복붙노트

[SPRING] Spring Security로 파일 업로드 MaxUploadSizeExceededException을 멋지게 처리하는 법

SPRING

Spring Security로 파일 업로드 MaxUploadSizeExceededException을 멋지게 처리하는 법

Spring Web 4.0.5, Spring Security 3.2.4, Commons FileUpload 1.3.1, Tomcat 7을 사용하고 있습니다. 업로드 크기 제한을 초과하면 "500 Internal Server Error"라는 결과를 초래할 수 있습니다. 추한 MaxUploadSizeExceededException이 발생합니다. . 멋진 일반 팝업으로 처리하지만 오히려 제 컨트롤러가 적절한 설명 메시지로 원래 양식으로 돌아가서 처리하도록합니다.

Spring Security를 ​​사용하지 않을 때 사용할 수있는 몇 가지 해결책을 통해 비슷한 질문을 여러 번 해 보았습니다. 내가 시험해 본 사람 중 누구도 나를 위해 일하지 않았다.

문제는 Spring Security를 ​​사용할 때 CommonsMultipartResolver가 "multipartResolver"빈으로 추가되지 않고 "filterMultipartResolver"로 추가 될 수 있습니다.

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

filterMultipartResolver.setResolveLazily (true)를 설정하면; 아무런 차이가 없습니다.

내 자신으로 CommonsMultipartResolver을 서브 클래스 화하고 MaxUploadSizeExceededException을 트랩하고 빈 MultipartParsingResult를 반환하는 무언가로 parseRequest () 메서드를 재정의하면 "403 Forbidden"오류가 발생합니다.

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를 구현할 필요가 없습니다.

더 나은 해결책을 찾지 못한다면 업로드 크기 제한을 제거하고 컨트롤러에서 직접 처리 할 것입니다. 사용자가 파일 크기에 대한 오류 메시지를보기 전에 업로드가 끝날 때까지 기다리라는 단점이 있습니다. 저는이 모든 것을 무시할 수도 있습니다. 왜냐하면이 경우에 이미지이기 때문에 적절한 값으로 크기를 조정할 수 있기 때문입니다.

그래도이 문제에 대한 해결책을보고 싶습니다.

고맙습니다

편집하다:

스택 추적을 요청대로 추가합니다. 500이 생성되는 경우입니다.

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

해결법

  1. ==============================

    1.추가 필터를 추가하여 예외를 catch하고 오류 페이지로 리디렉션함으로써 MaxUploadSizeExceededException을 처리 할 수 ​​있습니다. 예를 들어, 다음과 같이 MultipartExceptionHandler 필터를 작성할 수 있습니다.

    추가 필터를 추가하여 예외를 catch하고 오류 페이지로 리디렉션함으로써 MaxUploadSizeExceededException을 처리 할 수 ​​있습니다. 예를 들어, 다음과 같이 MultipartExceptionHandler 필터를 작성할 수 있습니다.

    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에서 처리되는 경우이 방법이 효과적입니다.

    그런 다음 MultipartFilter 전에이 필터를 추가 할 수 있습니다. 예를 들어 web.xml을 사용하는 경우 다음과 같은 내용이 표시됩니다.

    <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에서 다음을 수행 할 수 있습니다.

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

    추신 : 오류 처리를 논의하기 위해 문서를 업데이트하기 위해 SEC-2614를 만들었습니다.

  2. ==============================

    2.나는 파티에 늦었다는 것을 알고 있지만 훨씬 더 우아한 해결책을 찾았다.

    나는 파티에 늦었다는 것을 알고 있지만 훨씬 더 우아한 해결책을 찾았다.

    멀티 파트 분석기에 대한 필터를 추가하는 대신 Controller 메소드에서 MaxUploadSizeExceededException을 추가하고 web.xml에 DelegatingFilterProxy에 대한 필터를 추가하고 요청을 리디렉션하지 않고 컨트롤러에서 예외 핸들러를 추가 할 수 있습니다.

    e.f. :

    방법 (컨트롤러에서) :

    @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
    public ResponseEntity<String> uploadFile(MultipartHttpServletRequest request) throws MaxUploadSizeExceededException {
        //code
    }
    

    예외 처리기 (동일한 컨트롤러에서) :

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ResponseEntity handleSizeExceededException(HttpServletRequest request, Exception ex) {
        //code
    }
    

    Web.xml (Rob Winch에게 감사드립니다) :

    <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>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>ERROR</dispatcher>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
    

    그것이 필요한 전부입니다.

  3. ==============================

    3.문제는 다중 필터 이후에 springSecurityFilterChain을 추가해야한다는 것입니다. 그래서 당신은 403 상태를 얻고 있습니다. 이리:

    문제는 다중 필터 이후에 springSecurityFilterChain을 추가해야한다는 것입니다. 그래서 당신은 403 상태를 얻고 있습니다. 이리:

    http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html#csrf-multipartfilter

    그 후에는 @ExceptionHandler 주석이있는 메소드가 포함 된 @ControllerAdvice 주석이있는 클래스에서 FileUploadBase.SizeLimitExceededException을 catch 할 수 있다고 생각합니다.

  4. ==============================

    4.실험하는 동안 내놓은 해결책은 다음과 같습니다.

    실험하는 동안 내놓은 해결책은 다음과 같습니다.

    이렇게하면 크기가 초과 된 경우에도 양식 제출을 처리하는 데 일반적인 Controller 메서드를 사용할 수 있습니다.

    이 접근법이 마음에 들지 않는 것은 외부 라이브러리 패키지 (MultipartParsingResult가 보호됨)를 엉망으로 만들고 url (덜 안전한 btw) 형식으로 토큰을 설정해야한다는 것입니다.

    내가 좋아하는 것은 컨트롤러의 한 곳에서 양식 제출을 처리한다는 것입니다.

    큰 파일이 사용자에게 반환되기 전에 완전히 다운로드되는 문제는 지속되지만, 이미 다른 곳에서 해결 된 것 같습니다.

  5. from https://stackoverflow.com/questions/23856254/how-to-nicely-handle-file-upload-maxuploadsizeexceededexception-with-spring-secu by cc-by-sa and MIT license