在spring过滤器中解决multipart/form-data请求 [英] Resolving multipart/form-data request in spring filter

查看:1017
本文介绍了在spring过滤器中解决multipart/form-data请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在Spring MVC 3中开发自己的CSRF filter(有一些额外的培训使我做到了,这就是为什么我不考虑Spring安全性的原因.)

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.)

除了具有enctype="multipart/form-data"的形式,我的过滤器对所有形式都适用.因此我无法从正常的HttpServletRequest获取请求参数.

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.

我曾尝试将HttpServletRequest强制转换为MultipartHttpServletRequest,但我发现我也无法做到这一点.

I've tried casting HttpServletRequest to MultipartHttpServletRequest but I found out I can not do that either.

我的目标不是从请求中获取文件,而是仅获取名为csrf的简单表单输入. (我已经使用我的表格上传了文件)

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)

到目前为止,这是我的代码:

Here is my code till now:

CSRFilter

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

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>

我也使用springMultipartResolver过滤器...

and also I use springMultipartResolver filter ...

<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>

在multipart/form-data表单上尝试时,出现java.lang.IllegalStateException: Multipart request not initialized异常.

I get java.lang.IllegalStateException: Multipart request not initialized Exception when I try it on multipart/form-data forms.

我看了很多互联网上的例子.它们中的大多数用于文件上传目的,对我无济于事,我还尝试了不同的方法将HttpServletRequest强制转换为其他任何可以解决我的多部分请求的对象,但是我无法成功.

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强制转换为MultipartHttpServletRequest,因为您首先必须解决您的请求.

You can not cast HttpServletRequest to MultipartHttpServletRequest, because you first have to resolve your request.

我使用了CommonsMultipartResolver类,并使用commonsMultipartResolver.resolveMultipart(request)方法(其中请求是HttpServletRequest的类型)获得了MultipartHttpServletRequest

I used CommonsMultipartResolver Class and got MultipartHttpServletRequest using commonsMultipartResolver.resolveMultipart(request) method (where request is type of HttpServletRequest)

因此,这是我的 CSRF 类的checkPostedCsrfToken()方法:

So, here is my CSRF class, checkPostedCsrfToken() method:

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;
    }

但是,请注意,您最终将使用这种方法失去所有请求参数和数据.因此,您必须扩展HttpServletRequestWrapper类以读取请求字节,并使用它们获取参数(如果对您而言重要的是参数不会丢失抛出筛选器链).换句话说,您需要克隆您的请求.

But, Note that you 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.

这是我在StackOverflow中找到的一个好助手类,(我再也找不到问题了,如果找到它,我将对其进行编辑).

Here is a good helper class I found in StackOverflow, (I cant find the question again, I will edit this if I find it).

MultiReadHttpServletRequest

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();
        }
    }
}

现在您所需要做的就是在过滤器中使用MultiReadHttpServletRequest而不是常规的HttpServletRequest

now all you need to do is to use MultiReadHttpServletRequest instead of normal HttpServletRequest in filter :

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 :)

这篇关于在spring过滤器中解决multipart/form-data请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆