在我的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()
答案 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();
}