上传文件返回403错误 - Spring MVC

时间:2015-08-18 05:12:33

标签: java spring spring-mvc spring-security

在我的Spring 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视图

到目前为止,我已尝试在单独的类中初始化Spring Security过滤器之前指定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");
    }

}

我有一个servlet请求属性列表,它返回以下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中加入以下内容时,我会收到403

.csrf().disable()

4 个答案:

答案 0 :(得分:6)

Spring Security参考的CSRF - Multipart (File Upload)部分对此进行了介绍。您有两种选择:

在Spring Security之前放置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令牌

如果允许未经授权的用户上传临时文件是不可接受的,另一种方法是将MultipartFilter置于Spring Security过滤器之后,并将CSRF作为查询参数包含在表单的action属性中。 jsp的示例如下所示

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

这种方法的缺点是可能泄漏查询参数。更常见的是,将敏感数据放入正文或标题中以确保其不会泄露被认为是最佳做法。其他信息可在RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI’s

中找到

答案 1 :(得分:1)

对我来说,解决方案是像这样在csrf()中禁用WebSecurityConfig

 @EnableWebSecurity
 @Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
    }
}

现在我可以简单地上传多部分文件了:)

答案 2 :(得分:0)

对我来说,快速解决方案是以下

<%@ 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.";
        }
    }

}

我在spring调度程序文件中添加了以下代码

<!-- upload files -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

         <!-- setting maximum upload size -->
        <property name="maxUploadSize" value="100000" />

    </bean>

答案 3 :(得分:0)

对我来说,即使使用csrf()。disable()也无法正常工作。 一旦我禁用它,我得到200,但文件没有上传,我也没有看到任何错误。 一旦我设置了调试标志logging.level.org.springframework.web:DEBUG 我看到了根本原因:

[org.springframework.web.multipart.MultipartException:无法解析多部分servlet请求;嵌套异常为java.io.IOException:临时上传位置[/ target / tomcat / work / Tomcat / localhost / ROOT]无效]“,” exception“:”“}

我尝试在application.yaml上设置位置: spring.servlet.http.multipart.location:“ / tmp” 但是它并没有改变位置,所以我所做的是添加以下代码,并且达到了目的:

@Bean

MultipartConfigElement multipartConfigElement() {
    MultipartConfigFactory factory = new MultipartConfigFactory();
    String location = "/tmp";
    LOGGER.debug("Multipart location file:" + location);
    File tmpFile = new File(location);
    if (!tmpFile.exists()) {
        tmpFile.mkdirs();
    }
    factory.setLocation(location);
    return factory.createMultipartConfig();
}