前端
今天遇到一个问题,我的 form 表单写的没有错,用 js 生成的 action,不知道什么原因一直提交不上去。
<form method="post" class="form-horizontal" role="form" id="form">
</form>
function changeState(that){
var id = $(that).attr("applyid");
$("#applyId").val(id);
$("#form").attr("action","/feedbacks/"+id+"/state");
}
提交表单页面直接跳到登录页面,推测是请求给 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);
}
}