解决弹簧过滤器

时间:2016-02-14 14:15:54

标签: java spring forms spring-mvc servlets

我正在尝试在Spring MVC 3中编写和开发自己的CSRF过滤器(有一些额外的培训让我这样做,所以请不要建议我使用Spring Security。我知道,谢谢! )

我的过滤器适用于除enctype =“multipart / form-data”之外的所有表单,所以实际上我无法从普通的HttpServletRequest获取请求参数。

我已经尝试将它投射到MultipartHttpServletRequest,但我发现我也做不到。

请注意,我的目标不是获取文件,只是简单的表单输入名为“csrf”。我已经用我的表格上传了文件。

这是我的代码,直到现在:

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

我的调度员也有这条线:

<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过滤器......

<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异常。

我在互联网上看了很多例子。其中大部分用于文件上传目的并且无法帮助我,我也尝试了不同的方法将HttpServletRequest转换为任何其他对象,这些对象可以解决多部分请求,但我无法成功。

我该怎么做?

感谢。

2 个答案:

答案 0 :(得分:2)

您无法将MultipartHttpServletRequest投射到CommonsMultipartResolver,因为您首先必须解决您的请求。

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

所以,这是我的 CSRF 类,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

但是,请注意,您将丢失所有请求参数和数据。所以你必须扩展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(); } } } 类来读取请求字节并使用它们来获取参数,如果对你来说参数不会丢失抛出过滤器链。

这是我在StackOverflow中找到的一个很好的帮助器类,(我再也找不到问题了,如果找到它我会编辑它。)

MultiReadHttpServletRequest

MultiReadHttpServletRequest

现在您需要做的就是在过滤器中使用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; 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); } } }

function removeItem(id) {
    $.ajax({
        url:"removeItem_checkOut.php",  
        type:"GET",
        data:"id="+id,
        success:function(content) {
            window.location.reload(); 
        }
    });
}

我希望这有助于某人:)

答案 1 :(得分:2)

我需要能够在不损坏Servlet或后续过滤器的情况下检查Request的主体,因此我创建了一个小型项目。

罐子是&lt; 10kb,如果你使用的是Tomcat,那么除此之外你不需要任何东西。此外,它已获得MIT许可,因此您可以在任何可能需要的项目中使用它。

您可以在https://github.com/isapir/servlet-filter-utils

找到该项目

您所要做的就是用RereadableServletRequest包裹传入的请求,例如

HttpServletRequest requestWrapper = new RereadableServletRequest(servletRequest);