복붙노트

[SPRING] 업로드 파일 반환 403 오류 - 봄 MVC

SPRING

업로드 파일 반환 403 오류 - 봄 MVC

스프링 MVC 프로젝트에서 간단한 폼을 통해 파일을 업로드하려고합니다.

HTML 양식 :

<form method="POST" enctype="multipart/form-data" action="/upload">
    <label>Select File</label> 
    <input type="file" name="file"/>
</form>

내 컨트롤러 :

@Controller
public class FileController {
    @RequestMapping(value="/upload", method=RequestMethod.POST)
        public @ResponseBody String handleFileUpload(
                @RequestParam("name") String name,
                @RequestParam("file") MultipartFile file){
            if (!file.isEmpty()) {
                try {
                    //do stuff
                } catch (Exception e) {
                    return "You failed to upload " + name + " => " + e.getMessage();
                }
            } else {
                return "You failed to upload " + name + " because the file was empty.";
            }
        }
}

보안 구성 :

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/upload").permitAll()
            .and()
               .exceptionHandling().accessDeniedPage("/403")
    }
}

그러나 나는 403 : Forbidden 오류가 발생하고 매번 403.html보기로 리디렉션됩니다.

지금까지 스프링 보안 필터가 별도의 클래스로 초기화되기 전에 MultipartFilter를 지정하려고 시도했지만 행운은 없습니다.

public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
        insertFilters(servletContext, new MultipartFilter());
    }
}

어떤 아이디어?

업데이트 : 내 WebAppInitializer 포함

@Configuration
@Import({ WebSecurityConfig.class })
public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        System.out.println(":::Starting My App:::");
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(WebMVCConfig.class);
        context.setServletContext(servletContext);
        context.setConfigLocation("com.myApp.configuration");
    }

}

403 오류와 함께 다음을 반환하는 서블릿 요청 속성 목록이 있습니다.

javax.servlet.forward.request_uri
javax.servlet.forward.context_path
javax.servlet.forward.servlet_path
__spring_security_scpf_applied
org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE
SPRING_SECURITY_403_EXCEPTION
org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER
springMacroRequestContext
themes
thymeleafEvaluationContext
org.springframework.security.web.FilterChainProxy.APPLIED
_csrf
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.FILTERED
org.springframework.security.web.csrf.CsrfFilter@539743f9.FILTERED
beans
springRequestContext
org.springframework.web.servlet.HandlerMapping.introspectTypeLevelMapping
org.springframework.web.servlet.DispatcherServlet.FLASH_MAP_MANAGER
org.springframework.web.servlet.DispatcherServlet.CONTEXT
org.springframework.core.convert.ConversionService
execInfo
org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping
org.springframework.web.context.request.async.WebAsyncManager.WEB_ASYNC_MANAGER
org.springframework.web.servlet.resource.ResourceUrlProvider
org.springframework.web.servlet.DispatcherServlet.OUTPUT_FLASH_MAP
org.springframework.web.servlet.HandlerMapping.bestMatchingPattern
org.springframework.security.web.csrf.CsrfToken
org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER

업데이트 # 2 : 이것은 CSRF 문제 일 것입니다. WebSecurityConfig에 다음을 포함 시키면 아무런 결과도 얻지 못합니다.

.csrf().disable()

해결법

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

    1.이것은 Spring Security 레퍼런스의 CSRF - Multipart (File Upload) 섹션에서 다룹니다. 두 가지 옵션이 있습니다.

    이것은 Spring Security 레퍼런스의 CSRF - Multipart (File Upload) 섹션에서 다룹니다. 두 가지 옵션이 있습니다.

    스프링 보안 전에 MultipartFilter 배치하기

    첫 번째 옵션은 Spring Security 필터 전에 MultipartFilter가 지정되었는지 확인하는 것입니다. Spring Security 필터를 사용하기 전에 MultipartFilter를 지정하면 MultipartFilter를 호출 할 수있는 권한이 없다는 것을 의미합니다. 이는 누군가가 서버에 임시 파일을 배치 할 수 있음을 의미합니다. 그러나 권한이 부여 된 사용자 만 응용 프로그램에서 처리하는 파일을 제출할 수 있습니다. 일반적으로 이것은 임시 파일 업로드가 대부분의 서버에 무시할 수있는 영향을주기 때문에 권장되는 방법입니다.

    Java 구성을 사용하는 Spring Security 필터 전에 MultipartFilter가 지정되도록하려면 사용자는 beforeSpringSecurityFilterChain을 다음과 같이 무시할 수 있습니다.

    public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
    
        @Override
        protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
            insertFilters(servletContext, new MultipartFilter());
        }
    }
    

    XML 구성으로 Spring Security 필터 앞에 MultipartFilter가 지정되도록하기 위해, 사용자는 MultipartFilter의 요소가 web.xml 내의 springSecurityFilterChain 앞에 위치하도록 보장 할 수 있습니다.

    <filter>
        <filter-name>MultipartFilter</filter-name>
        <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
    </filter>
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MultipartFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    실행중인 CSRF 토큰 포함

    권한이없는 사용자가 임시 파일을 업로드하는 것을 허용 할 수 없다면, Spring Security 필터 다음에 MultipartFilter를 놓고 CSRF를 쿼리 매개 변수로 폼의 action 속성에 포함 시키십시오. jsp를 사용한 예가 아래에 나와 있습니다.

    <form action="./upload?${_csrf.parameterName}=${_csrf.token}" 
          method="post" 
          enctype="multipart/form-data">
    

    이 접근법의 단점은 쿼리 매개 변수가 유출 될 수 있다는 것입니다. 더 진부하게, 유출되지 않도록 본문 또는 헤더 내에 중요한 데이터를 배치하는 것이 가장 좋습니다. 추가 정보는 RFC 2616 섹션 15.1.3 URI의 중요한 정보 인코딩에 있습니다.

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

    2.해결책은 WebSecurityConfig에서 csrf ()를 다음과 같이 비활성화하는 것입니다.

    해결책은 WebSecurityConfig에서 csrf ()를 다음과 같이 비활성화하는 것입니다.

     @EnableWebSecurity
     @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.cors().and().csrf().disable();
        }
    }
    

    그리고 이제는 간단히 멀티 파트 파일을 업로드 할 수 있습니다. :)

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

    3.나를위한 빠른 해결책은 다음과 같았습니다.

    나를위한 빠른 해결책은 다음과 같았습니다.

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ page session="false" %>
    <html>
    <head>
    <title>Upload File Request Page</title>
    </head>
    <body>
        <form method="POST" action="file/uploadFile?${_csrf.parameterName}=${_csrf.token}" enctype="multipart/form-data">
            File to upload: <input type="file" name="file"><br /> 
            Name: <input type="text" name="name"><br /> <br /> 
            <input type="submit" value="Upload"> Press here to upload the file!
        </form> 
    </body>
    </html>
    

    컨트롤러 코드는 다음과 같습니다.

    package com.student.controller;
    
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.security.Principal;
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.multipart.MultipartFile;
    
    @Controller
    @RequestMapping("/file")
    public class FileUploadController {
    
        @RequestMapping(value = "", method = RequestMethod.GET)
        public String index(ModelMap modelMap,Principal principal,HttpServletRequest request) {
            return "uploadfile";
        }
    
        @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
        public @ResponseBody String uploadFileHandler(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) {
    
            if (!file.isEmpty()) {
                try {
                    byte[] bytes = file.getBytes();
    
                    // Creating the directory to store file
                    String rootPath = System.getProperty("catalina.home");
                    File dir = new File(rootPath + File.separator + "tmpFiles");
                    if (!dir.exists())
                        dir.mkdirs();
    
                    // Create the file on server
                    File serverFile = new File(dir.getAbsolutePath()
                            + File.separator + name);
                    BufferedOutputStream stream = new BufferedOutputStream(
                            new FileOutputStream(serverFile));
                    stream.write(bytes);
                    stream.close();
    
    
    
                    return "You successfully uploaded file=" + rootPath+name;
                } catch (Exception e) {
                    return "You failed to upload " + name + " => " + e.getMessage();
                }
            } else {
                return "You failed to upload " + name
                        + " because the file was empty.";
            }
        }
    
    }
    

    봄 디스패처 파일에 다음 코드를 추가했습니다.

    <!-- upload files -->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    
             <!-- setting maximum upload size -->
            <property name="maxUploadSize" value="100000" />
    
        </bean>
    
  4. from https://stackoverflow.com/questions/32064000/uploading-file-returns-403-error-spring-mvc by cc-by-sa and MIT license