Spring Security和多部分请求

23
我是一位有用的助手,可以为您进行文本翻译。
我有一个受Spring Security和OAuth2保护的@Controller,我正在尝试让我的用户上传文件:
@Controller
@RequestMapping(value = "/api/image")
public class ImageController {

    @PreAuthorize("hasAuthority('ROLE_USER')")
    @RequestMapping(value = "/upload", method = RequestMethod.PUT)
    public @ResponseBody Account putImage(@RequestParam("title") String title, MultipartHttpServletRequest request, Principal principal){
        // Some type of file processing...
        System.out.println("-------------------------------------------");
        System.out.println("Test upload: " + title);
        System.out.println("Test upload: " + request.getFile("file").getOriginalFilename());
        System.out.println("-------------------------------------------");

        return ((Account) ((OAuth2Authentication) principal).getPrincipal());
    }
}

当我尝试上传文件和标题时,出现了以下异常。我将Content-Type头设置为multipart/form-data。
java.lang.IllegalStateException: Current request is not of type [org.springframework.web.multipart.MultipartHttpServletRequest]: SecurityContextHolderAwareRequestWrapper[ FirewalledRequest[ org.apache.catalina.connector.RequestFacade@1aee75b7]]
    at org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver.resolveArgument(ServletRequestMethodArgumentResolver.java:84)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:75)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:156)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:117)

如何在Spring Security后面进行文件上传?似乎请求从未被转换为MultiPartHttpServerRequest,因此无法正常工作?

如果我更改我的方法签名以使用@RequestParam MultipartFile,则会出现异常,例如:

DEBUG DefaultListableBeanFactory - Returning cached instance of singleton bean 'imageController'
DEBUG ExceptionHandlerExceptionResolver - Resolving exception from handler [public com.tinsel.server.model.Account com.tinsel.server.controller.ImageController.putImage(java.lang.String,org.springframework.web.multipart.MultipartFile,java.security.Principal)]: java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
DEBUG ResponseStatusExceptionResolver - Resolving exception from handler [public com.tinsel.server.model.Account com.tinsel.server.controller.ImageController.putImage(java.lang.String,org.springframework.web.multipart.MultipartFile,java.security.Principal)]: java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
DEBUG DefaultHandlerExceptionResolver - Resolving exception from handler [public com.tinsel.server.model.Account com.tinsel.server.controller.ImageController.putImage(java.lang.String,org.springframework.web.multipart.MultipartFile,java.security.Principal)]: java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
DEBUG DispatcherServlet - Could not complete request
java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
    at org.springframework.util.Assert.notNull(Assert.java:112)

...但我在XML中配置了MultipartResolver:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="268435456"/> <!-- 256 megs -->
</bean>

我看到了关于在Spring 3.0下使之工作的这篇博客文章,但我正在尝试保持更加更新,目前正在使用3.1。也许有更新的修复方法吗?

4个回答

25
问题在于我使用了PUT而不是POST。Commons FileUpload硬编码只接受POST请求上传文件。
请检查isMultipartContent方法。要解决此问题,请使用POST或扩展该类并覆盖该方法以按您的要求工作。
我为此问题打开了FILEUPLOAD-214

4
FILEUPLOAD-214问题的解决方式为WONTFIX。根据作者的说法,不应将PUT与Multipart一起使用。 - beerbajay
1
是的,最终我改成了POST而不是PUT。 - Matthew Runo
已在1.3版本中修复。 - thomaux
3
3年后在1.3.1版本中,它仍然存在 - 只有POST - dae.eklen
1
这个问题仍然存在。这只是一个微小的改变,不会违反任何HTTP规范(它违反了REST,值得注意的是REST不是HTTP,因此不定义多部分请求或PUT,在这里可以安全地忽略),但是...人们坚持不这样做。由于某种原因。很高兴看到愚蠢在各个地方都很活跃。 - anon

3
为了解决这个问题,不要使用Spring的MultiPartHttpServerRequest,而是将请求作为HttpServletRequest处理,使用apache commons fileupload库来解析PUT方法的请求,并处理文件。以下是一些示例代码:
ServletFileUpload fileUpload = new ServletFileUpload(new DiskFileItemFactory());
List<FileItem> fileItems = fileUpload.parseRequest(httpServletRequest);
InputStream in = fileItems.get(0).getInputStream();
...

如何在 HTTPRequest 中获取文件,请帮忙 :P - Dupinder Singh

2
在Config.groovy中,请确保启用了multipart。
(注:此处未提供具体的格式要求,如有需要请补充说明)
// whether to disable processing of multi part requests
   grails.web.disable.multipart=false

在控制器中添加“Post”方法。
def upload(){
    MultipartHttpServletRequest mpr = (MultipartHttpServletRequest)request;
    if(request instanceof MultipartHttpServletRequest)
            {
                CommonsMultipartFile f = (CommonsMultipartFile) mpr.getFile("myFile");
                println f.contentType
                f.transferTo()
                if(!f.empty)
                    flash.message = 'success'
                else
                    flash.message = 'file cannot be empty'
            }
    else
    flash.message = 'request is not of type MultipartHttpServletRequest'}

通过这些,我能够上传文件,与Spring Security无关。

-1

你可以看一下https://github.com/joshlong/the-spring-tutorial,其中有一个示例演示了如何在启用Spring Security OAuth的情况下发布到Spring MVC。我甚至使用HTML5拖放将图像拖到屏幕上,然后通过ajax提交到服务器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接