java 解决 spring 过滤器中的多部分/表单数据请求
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35392795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Resolving multipart/form-data request in spring filter
提问by Sep GH
I'm trying to develop my own CSRF filter
in Spring MVC 3 (There are some extra trainings that made me do that, thats why Im not considering spring security.)
我正在尝试CSRF filter
在 Spring MVC 3 中开发我自己的(有一些额外的培训让我这样做,这就是为什么我不考虑 spring 安全性。)
My filter works fine with all forms except those that have enctype="multipart/form-data"
. So I can not get request parameters from normal HttpServletRequest.
我的过滤器适用于所有形式,但带有enctype="multipart/form-data"
. 所以我无法从普通的 HttpServletRequest 获取请求参数。
I've tried casting HttpServletRequest
to MultipartHttpServletRequest
but I found out I can not do that either.
我试过投射HttpServletRequest
到,MultipartHttpServletRequest
但我发现我也做不到。
My objective is not getting files from the request, but to only get simple form input named csrf
. (Ive already uploaded files with my forms)
我的目标不是从请求中获取文件,而是只获取名为csrf
. (我已经用我的表格上传了文件)
Here is my code till now:
这是我的代码,直到现在:
CSRFilter
CSR过滤器
public class CSRFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
CSRF csrf = new CSRF(req);
if(csrf.isOk()){
chain.doFilter(req, res);
}else {
//todo : Show Error Page
String redirect = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/access-forbidden";
response.sendRedirect(redirect);
}
}
}
CSRF
CSRF
public class CSRF {
HttpServletRequest request;
ServletRequest req;
String token;
boolean ok;
private static final Logger logger = Logger.getLogger(CSRF.class);
public CSRF(ServletRequest request) {
this.request = (HttpServletRequest) request;
this.req = request;
init();
}
public CSRF() {
}
public void setRequest(HttpServletRequest request) {
this.request = (HttpServletRequest) request;
this.req = request;
init();
}
private void init() {
if (request.getMethod().equals("GET")) {
generateToken();
addCSRFTokenToSession();
addCSRFTokenToModelAttribute();
ok = true;
} else if (request.getMethod().equals("POST")) {
if (checkPostedCsrfToken()) {
ok = true;
}
}
}
private void generateToken() {
String token;
java.util.Date date = new java.util.Date();
UUID uuid = UUID.randomUUID();
token = uuid.toString() + String.valueOf(new Timestamp(date.getTime()));
try {
this.token = sha1(token);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
this.token = token;
}
}
private void addCSRFTokenToSession() {
request.getSession().setAttribute("csrf", token);
}
private void addCSRFTokenToModelAttribute() {
request.setAttribute("csrf", token);
}
private boolean checkPostedCsrfToken() {
System.out.println("____ CSRF CHECK POST _____");
if (request.getParameterMap().containsKey("csrf")) {
String csrf = request.getParameter("csrf");
if (csrf.equals(request.getSession().getAttribute("csrf"))) {
return true;
}
}else {
//Check for multipart requests
MultipartHttpServletRequest multiPartRequest = new DefaultMultipartHttpServletRequest((HttpServletRequest) req);
if (multiPartRequest.getParameterMap().containsKey("csrf")) {
String csrf = multiPartRequest.getParameter("csrf");
if (csrf.equals(request.getSession().getAttribute("csrf"))) {
return true;
}
}
}
log();
return false;
}
private void log() {
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
if(username==null){
username = "unknown (not logged in)";
}
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
String userAgent = request.getHeader("User-Agent");
String address = request.getRequestURI();
System.out.println("a CSRF attack detected from IP: " + ipAddress + " in address \"" + address + "\" - Client User Agent : " + userAgent + " Username: " + username);
logger.error("a CSRF attack detected from IP: " + ipAddress + " in address \"" + address + "\" - Client User Agent : " + userAgent + " Username: " + username);
}
public boolean isOk() {
return ok;
}
static String sha1(String input) throws NoSuchAlgorithmException {
MessageDigest mDigest = MessageDigest.getInstance("SHA1");
byte[] result = mDigest.digest(input.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < result.length; i++) {
sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
}
I have this line in my dispatcher too :
我的调度员中也有这条线:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="40000000"/>
</bean>
and also I use springMultipartResolver filter ...
而且我还使用 springMultipartResolver 过滤器...
<filter>
<display-name>springMultipartFilter</display-name>
<filter-name>springMultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springMultipartFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</filter>
I get java.lang.IllegalStateException: Multipart request not initialized
Exception when I try it on multipart/form-data forms.
我得到java.lang.IllegalStateException: Multipart request not initialized
的异常当我尝试它的multipart / form-data的形式。
I looked at many Examples in internet. Most of them was for file uploading purpose and could not help me, I also tried different ways to cast HttpServletRequest to any other object that gives me resolved multipart request, But I could not succeed.
我查看了互联网上的许多示例。他们中的大多数是为了文件上传目的而无法帮助我,我也尝试了不同的方法将 HttpServletRequest 转换为任何其他对象,该对象为我解决了多部分请求,但我无法成功。
How can I do it ?
我该怎么做 ?
Thanks.
谢谢。
回答by Sep GH
You can not cast HttpServletRequest
to MultipartHttpServletRequest
, because you first have to resolve your request.
您不能强制转换HttpServletRequest
为MultipartHttpServletRequest
,因为您首先必须解决您的请求。
I used CommonsMultipartResolver
Class and got MultipartHttpServletRequest
using commonsMultipartResolver.resolveMultipart(request)
method (where request is type of HttpServletRequest
)
我使用了CommonsMultipartResolver
Class 并MultipartHttpServletRequest
使用了commonsMultipartResolver.resolveMultipart(request)
方法(其中请求是类型HttpServletRequest
)
So, here is my CSRFclass, checkPostedCsrfToken()
method:
所以,这是我的CSRF类,checkPostedCsrfToken()
方法:
private boolean checkPostedCsrfToken() {
if (request.getParameterMap().containsKey("csrf")) {
String csrf = request.getParameter("csrf");
if (csrf.equals(request.getSession().getAttribute("csrf"))) {
return true;
}
} else if (request.getContentType() != null && request.getContentType().toLowerCase().contains("multipart/form-data")) {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
MultipartHttpServletRequest multipartRequest = commonsMultipartResolver.resolveMultipart(request);
if (multipartRequest.getParameterMap().containsKey("csrf")) {
String csrf = multipartRequest.getParameter("csrf");
if (csrf.equals(request.getSession().getAttribute("csrf"))) {
return true;
}
}
}
log();
return false;
}
But, Note thatyou will end up loosing all request parameters and data with this approach. So you have to extend HttpServletRequestWrapper
class to read request bytes and use them to get parameters if it matters to you that parameters don't get lost throw filter chain. In other words, you need a clone of your request.
但是,请注意,使用这种方法最终会丢失所有请求参数和数据。因此,您必须扩展HttpServletRequestWrapper
类以读取请求字节并使用它们来获取参数,如果参数不会丢失抛出过滤器链对您来说很重要。换句话说,您需要一个请求的副本。
Here is a good helper class I found in StackOverflow, (I cant find the question again, I will edit this if I find it).
这是我在 StackOverflow 中找到的一个很好的帮助类,(我又找不到问题了,如果找到我会编辑它)。
MultiReadHttpServletRequest
多读HttpServletRequest
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
public MultiReadHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null)
cacheInputStream();
return new CachedServletInputStream();
}
@Override
public BufferedReader getReader() throws IOException{
return new BufferedReader(new InputStreamReader(getInputStream()));
}
private void cacheInputStream() throws IOException {
/* Cache the inputstream in order to read it multiple times. For
* convenience, I use apache.commons IOUtils
*/
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
/* An inputstream which reads the cached request body */
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
/* create a new input stream from the cached request body */
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
@Override
public int read() throws IOException {
return input.read();
}
}
}
now all you need to do is to use MultiReadHttpServletRequest
instead of normal HttpServletRequest
in filter :
现在你需要做的就是在 filter 中使用MultiReadHttpServletRequest
而不是 normal HttpServletRequest
:
public class CSRFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// The important part!! wrap the request:
MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request);
CSRF csrf = new CSRF(multiReadHttpServletRequest);
if(csrf.isOk()){
chain.doFilter(multiReadHttpServletRequest, res);
}else {
//todo : Show Error Page
String redirect = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/access-forbidden";
response.sendRedirect(redirect);
}
}
}
I wish this helps someone :)
我希望这对某人有所帮助:)
回答by isapir
I needed to be able to inspect the Request's body without damaging it for the Servlet or subsequent Filters, so I created a mini-project that does just that.
我需要能够在不损坏 Servlet 或后续过滤器的情况下检查请求的主体,因此我创建了一个小型项目来完成此任务。
The jar is < 10kb, and if you're using Tomcat then you don't need anything beyond that. Also, it's MIT licensed so you can use it in whatever project you may need.
jar 小于 10kb,如果您使用的是 Tomcat,那么除此之外您不需要任何东西。此外,它已获得 MIT 许可,因此您可以在您可能需要的任何项目中使用它。
You can find the project at https://github.com/isapir/servlet-filter-utils
您可以在https://github.com/isapir/servlet-filter-utils找到该项目
All you have to do is wrap the incoming Request with RereadableServletRequest
, e.g.
您所要做的就是用 包装传入的请求RereadableServletRequest
,例如
HttpServletRequest requestWrapper = new RereadableServletRequest(servletRequest);