1. 出错的信息描述
HTTP Status 500 – Internal Server Error
MESSAGE
:
Request processing failed; nested exception is com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:9090/uploads/91e5118e60924b21bd48a9ed19ab91ff_girl8.jpg returned a response status of 405 Method Not Allowed
Description
:
The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:9090/uploads/91e5118e60924b21bd48a9ed19ab91ff\_girl8.jpg returned a response status of 405 Method Not Allowed
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:986)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:881)
javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855)
javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
2. 出错的Project描述
使用的IDE: IDEA 2019-02
充当上传文件的Project hierarchy(Client):
接收文件的Project hierarchy(Server):
3. springmvc fileupload源码
controller
/**
* 跨服务器文件上传
* @return success
*/
@RequestMapping("/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
System.out.println("service id doing...");
// 定义上传文件服务器路径
String path = "http://localhost:9090/uploads/";
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 创建客户端的对象
Client client = Client.create();
// 和图片服务器进行连接
WebResource webResource = client.resource(path+filename);
// 上传文件
webResource.put(upload.getBytes());
return "success";
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<form action="user/fileupload1" method="post" enctype="multipart/form-data">
传统方式的文件上传:<input type="file" name="upload">
<br>
<input type="submit" value="上载">
</form>
<hr>
<form action="user/fileupload2" method="post" enctype="multipart/form-data">
SpringMVC方式的文件上传:<input type="file" name="upload">
<br>
<input type="submit" value="上载">
</form>
<hr>
<form action="user/fileupload3" method="post" enctype="multipart/form-data">
跨服务器的文件上传:<input type="file" name="upload">
<br>
<input type="submit" value="上载">
</form>
</body>
</html>
4. 出错分析
从 "1.出错的信息描述" 可以知道我们的错误在于PUT请求被用于接收文件的TOMCAT拒绝了。
请查看Apache Tomcat 9 (9.0.30) - Default Servlet Reference。我们需要注意到以下信息:
4.1 webapp启动后default servlet会加载
So by default, the default servlet is loaded at webapp startup and directory listings are disabled and debugging is turned off.
4.2 DefaultServlet的initParameters中readonly默认为true
以下为readonly
property的描述
Is this context "read only", so HTTP commands like PUT and DELETE are rejected? [true]
这意味着接收文件的TOMCAT默认加载DefaultServlet后,默认拒绝接收PUT和DELETE请求。因此我们需要改变initParameters中的readonly = false。
关于为什么readonly默认为true这一点,你可以先看看TOMCAT对于把readonly设置为false持有什么样的看法,以下为参考链接:
Apache Tomcat RCE if readonly set to false (CVE-2017-12617) - Alphabot Security
The Apache Tomcat team announced today that all Tomcat versions before 9.0.1 (Beta), 8.5.23, 8.0.47 and 7.0.82 contain a potentially dangerous remote code execution (RCE) vulnerability on all operating systems if the default servlet is configuredwith the parameterreadonly
set tofalse
or the WebDAV servlet is enabled with the parameterreadonly
set tofalse
. This configuration would allow any unauthenticated user to upload files (as used in WebDAV). It was discovered that the filter that prevents the uploading of JavaServer Pages (.jsp) can be circumvented. So JSPs can be uploaded, which then can be executed on the server.Now since this feature is typically not wanted, most publicly exposed system won’t have
readonly
set tofalse
.
不难推断出TOMCAT考虑到安全性,默认关闭了TOMCAT的PUT和DELETE请求(即readonly = true)。
5. 解决方法
根据 "4.出错分析" 可以知道我们需要修改web.xml文件的readonly参数。
我们需要在DefaultServlet的init-param中添加readonly为false(如果你的配置文件没有readonly,那么你需要手动添加readonly=false)
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
启动两个TOMCAT,现在展示效果
index.jsp(Client端):
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。