前端

今天遇到一个问题,我的 form 表单写的没有错,用 js 生成的 action,不知道什么原因一直提交不上去。

  1. <form method="post" class="form-horizontal" role="form" id="form">
  2. </form>
  1. function changeState(that){
  2. var id = $(that).attr("applyid");
  3. $("#applyId").val(id);
  4. $("#form").attr("action","/feedbacks/"+id+"/state");
  5. }

提交表单页面直接跳到登录页面,推测是请求给 shiro 拦截了。

// shiro 拦截器
filterMap.put("csrf", new CsrfTokenFilter());
filterChainDefinitionMap.put("/**", "authc,csrf");

最后随便加了个action。

<form method="post" class="form-horizontal" role="form" id="form" th:action="@{/user}">
</form>

html审查元素的时候,页面会增加一个csrfToken,可以正常提交了。

<input type="hidden" name="csrfToken" value="2bdaaca5-ebf9-450f-a25a-8faacd018965">

后台

package com.netmatch.drug.admin.shiro;

import com.google.common.collect.Maps;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.support.RequestDataValueProcessor;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.regex.Pattern;

@Component("requestDataValueProcessor")
public class CsrfRequestDataValueProcessor implements RequestDataValueProcessor {
    private Pattern DISABLE_CSRF_TOKEN_PATTERN = Pattern.compile("(?i)^(GET|HEAD|TRACE|OPTIONS)$");
    private String DISABLE_CSRF_TOKEN_ATTR = "DISABLE_CSRF_TOKEN_ATTR";

    @Override
    public String processAction(HttpServletRequest request, String action, String method) {
        if (method != null && this.DISABLE_CSRF_TOKEN_PATTERN.matcher(method).matches()) {
            request.setAttribute(this.DISABLE_CSRF_TOKEN_ATTR, Boolean.TRUE);
        } else {
            request.removeAttribute(this.DISABLE_CSRF_TOKEN_ATTR);
        }
        return action;
    }

    @Override
    public String processFormFieldValue(HttpServletRequest request, String name, String value, String type) {
        // 原样返回value
        return value;
    }

    @Override
    public Map<String, String> getExtraHiddenFields(HttpServletRequest request) {
        // 此处是当使用spring的taglib标签<form:form>创建表单时候,增加的隐藏域参数
        Map<String, String> hiddenFields = Maps.newHashMap();
        hiddenFields.put(CsrfTokenManager.CSRF_PARAM_NAME,
            CsrfTokenManager.createTokenForSession(request.getSession()));
        return hiddenFields;
    }

    @Override
    public String processUrl(HttpServletRequest request, String url) {
        // 原样返回url
        return url;
    }
}

图片上传接口不过滤

package com.netmatch.drug.admin.shiro;

import com.google.common.collect.Lists;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.UserFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Component
public class CsrfTokenFilter extends UserFilter {
    private static final String HEADER_POST = "POST";
    private static final List<String> IGNORE_URL = Lists.newArrayList("/files/image");

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (!HEADER_POST.equals(((HttpServletRequest) request).getMethod())) {
            return true;
        }
        if(IGNORE_URL.contains(((ShiroHttpServletRequest) request).getRequestURI())){
            return true;
        }
        String csrfToken = CsrfTokenManager.getTokenFromRequest(request);
        if (csrfToken == null || !csrfToken.equals(httpRequest.getSession().getAttribute(
            CsrfTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME))) {
            return false;
        }
        httpRequest.getSession().removeAttribute(CsrfTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
        return true;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        Subject currentUser = SecurityUtils.getSubject();
        if (currentUser != null) {
            currentUser.logout();
        }
        this.redirectToLogin(request, response);
        return false;
    }

}
package com.netmatch.drug.admin.shiro;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpSession;
import java.util.UUID;

public class CsrfTokenManager {
    // 隐藏域参数名称
    public static final String CSRF_PARAM_NAME = "csrfToken";
    // session中csrfToken参数名称
    public static final String CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CsrfTokenManager.class.getName() + ".tokenval";

    private CsrfTokenManager() {
    }

    // 在session中创建csrfToken
    public static String createTokenForSession(HttpSession session) {
        String token = null;
        synchronized (session) {
            token = (String) session
                .getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
            if (null == token) {
                token = UUID.randomUUID().toString();
                session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
            }
        }
        return token;
    }

    public static String getTokenFromRequest(ServletRequest request) {
        return request.getParameter(CSRF_PARAM_NAME);
    }
}