package com.yoro.core.web;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
/**
* 防止表單重複送出 FORM TOKEN 過濾器 作用:1.防止使用者後退操作,2.防止使用者重複重新整理操作
* <li>
* 利用TOKEN 防止重複送出需要結合特定場景,不要随意亂用
* 1.有寫業務是允許使用者插入重複資料的,沒必要使用
* 2.有些業務是有狀态控制的 重複送出有引發業務及驗證 也沒必要使用
* 3.如類是于淘寶下單業務是不允許通過一個form 重複送出下單的 則可以利用token實作
* <li>
* 使用注意:
* <li>
* 1.如果結合springmvc 等其他mvc架構 使用 注意 addUrl 路徑 不能 和 validUrl 路徑 相同
* 隻通過 get | post 送出方式區分調用的 action method 否則 點選後退安全不會讀取浏覽器緩存
* </li>
* @author zoro
*/
public class XsrfTokenRequestFilter implements Filter {
/**
* addUrls ,validUrls , processUrls 三個數組長度必須相等一一對應
*/
private String[] addUrls; // 需要增加 TOKEN 的頁面
private String[] validUrls;// 需要驗證TOEKN 的頁面
private String[] processUrls; // TOKEN 驗證失敗處理的頁面
FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
// 傳入的參數分割符号
String splitChar = filterConfig.getInitParameter("splitChar");
if (StringUtils.isNotBlank(filterConfig.getInitParameter("addUrls"))) {
addUrls = filterConfig.getInitParameter("addUrls").split(splitChar);
}
if (StringUtils.isNotBlank(filterConfig.getInitParameter("validUrls"))) {
validUrls = filterConfig.getInitParameter("validUrls").split(
splitChar);
}
if (StringUtils
.isNotBlank(filterConfig.getInitParameter("processUrls"))) {
processUrls = filterConfig.getInitParameter("processUrls").split(
splitChar);
}
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 擷取請求路徑
String path = getRelativePath((HttpServletRequest) request);
// 如果路徑存在增加的urls清單裡面,則向用戶端增加一個token 參數
if (ArrayUtils.contains(addUrls, path)) {
xsrfTokenGenerator.save((HttpServletRequest) request,
(HttpServletResponse) response);
}
// 如果路徑存在驗證urls清單裡面 這擷取用戶端 token 參數做驗證
if (ArrayUtils.contains(validUrls, path)) {
if (!xsrfTokenGenerator.validate((HttpServletRequest) request,
(HttpServletResponse) response)) {
// 驗證沒有通過則跳轉到錯誤處理頁面
((HttpServletResponse) response)
.sendRedirect(processUrls[ArrayUtils.indexOf(validUrls,
path)]);
return;
}
}
// 到了這裡說明驗證已經通過,則将用戶端 token 删除 下次送出用戶端cookie token 為空 重複送出則失敗
if (ArrayUtils.contains(validUrls, path)) {
xsrfTokenGenerator.remove((HttpServletRequest) request,
(HttpServletResponse) response);
}
chain.doFilter(request, response);
}
protected String getRelativePath(HttpServletRequest request) {
if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
String result = (String) request
.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
if (result == null) {
result = (String) request
.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
} else {
result = (String) request
.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH)
+ result;
}
if ((result == null) || (result.equals(""))) {
result = "/";
}
return (result);
}
// No, extract the desired path directly from the request
String result = request.getPathInfo();
if (result == null) {
result = request.getServletPath();
} else {
result = request.getServletPath() + result;
}
if ((result == null) || (result.equals(""))) {
result = "/";
}
return (result);
}
// 執行個體化 token 生成器
private XsrfTokenGenerator xsrfTokenGenerator = new SimpleXsrfTokenGenerator();
}
package com.yoro.core.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface XsrfTokenGenerator {
/**
* 增加 TOKEN
*/
public String save(HttpServletRequest request, HttpServletResponse response);
/**
* 删除TOKEN
*/
public void remove(HttpServletRequest request, HttpServletResponse response);
/**
* 驗證TOKEN 是否有效 驗證成功應當手動調用 remove 方法 手動從COOKIE中清除TOKEN
*/
public boolean validate(HttpServletRequest request, HttpServletResponse response);
}
package com.yoro.core.web;
import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import com.common.web.CookieUtils;
/**
*
* 基于cookie 的 利用TOOKEN 防止表單重複送出的實作
*
* @author zoro
*
*/
public class SimpleXsrfTokenGenerator implements XsrfTokenGenerator {
/**
* 預設TOOKEN 名稱
*/
private final static String FORM_TOEKN_NAME = "_form_token";
@Override
public boolean validate(HttpServletRequest request,
HttpServletResponse response) {
String paramString = request.getParameter(FORM_TOEKN_NAME);
if (StringUtils.isBlank(paramString)) {
return false;
}
String value = get(request);
if (StringUtils.isBlank(value)) {
return false;
}
return value.equals(paramString);
}
@Override
public String save(HttpServletRequest request, HttpServletResponse response) {
String value = StringUtils.remove(UUID.randomUUID().toString(), "-");
if (value != null) {
set(request, response, value);
}
return value;
}
@Override
public void remove(HttpServletRequest request, HttpServletResponse response) {
String value = get(request);
if (value != null) {
cancle(request, response);
}
}
/**
* 從cookie中擷取TOKEN
*
* @param request
* @return
*/
private String get(HttpServletRequest request) {
Cookie cookie = CookieUtils.getCookie(request, FORM_TOEKN_NAME);
return cookie == null ? null : cookie.getValue();
}
/**
* 設定 TOKEN 到 COOKIE 當中
*
* @param request
* @param response
* @param value
*/
private void set(HttpServletRequest request, HttpServletResponse response,
String value) {
if (value != null) {
CookieUtils.addCookie(request, response, FORM_TOEKN_NAME, value,
-1, null);
}
if (value != null) {
request.setAttribute(FORM_TOEKN_NAME, value);
}
}
/**
* 從COOKIE中清楚TOKEN
*
* @param request
* @param response
*/
private void cancle(HttpServletRequest request, HttpServletResponse response) {
CookieUtils.cancleCookie(request, response, FORM_TOEKN_NAME, null);
}
}
package com.common.web;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.util.Assert;
/**
* Cookie 輔助類
*
* @author hp
*
*/
public class CookieUtils {
/**
* 每頁條數cookie名稱
*/
public static final String COOKIE_PAGE_SIZE = "_cookie_page_size";
/**
* 預設每頁條數
*/
public static final int DEFAULT_SIZE = 20;
/**
* 最大每頁條數
*/
public static final int MAX_SIZE = 200;
/**
* 獲得cookie的每頁條數
*
* 使用_cookie_page_size作為cookie name
*
* @param request
* HttpServletRequest
* @return default:20 max:200
*/
public static int getPageSize(HttpServletRequest request) {
Assert.notNull(request);
Cookie cookie = getCookie(request, COOKIE_PAGE_SIZE);
int count = 0;
if (cookie != null) {
if (NumberUtils.isDigits(cookie.getValue())) {
count = Integer.parseInt(cookie.getValue());
}
}
if (count <= 0) {
count = DEFAULT_SIZE;
} else if (count > MAX_SIZE) {
count = MAX_SIZE;
}
return count;
}
/**
* 獲得cookie
*
* @param request
* HttpServletRequest
* @param name
* cookie name
* @return if exist return cookie, else return null.
*/
public static Cookie getCookie(HttpServletRequest request, String name) {
Assert.notNull(request);
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie c : cookies) {
if (c.getName().equals(name)) {
return c;
}
}
}
return null;
}
/**
* 根據部署路徑,将cookie儲存在根目錄。
*
* @param request
* @param response
* @param name
* @param value
* @param expiry
* @param domain
* @return
*/
public static Cookie addCookie(HttpServletRequest request,
HttpServletResponse response, String name, String value,
Integer expiry, String domain) {
Cookie cookie = new Cookie(name, value);
if (expiry != null) {
cookie.setMaxAge(expiry);
}
if (StringUtils.isNotBlank(domain)) {
cookie.setDomain(domain);
}
String ctx = request.getContextPath();
cookie.setPath(StringUtils.isBlank(ctx) ? "/" : ctx);
response.addCookie(cookie);
return cookie;
}
/**
* 取消cookie
*
* @param request
* @param response
* @param name
* @param domain
*/
public static void cancleCookie(HttpServletRequest request,
HttpServletResponse response, String name, String domain) {
Cookie cookie = new Cookie(name, "");
cookie.setMaxAge(0);
String ctx = request.getContextPath();
cookie.setPath(StringUtils.isBlank(ctx) ? "/" : ctx);
if (StringUtils.isNotBlank(domain)) {
cookie.setDomain(domain);
}
response.addCookie(cookie);
}
}
<[email protected]分隔 -->
<filter>
<filter-name>xsrfTokenRequestFilter</filter-name>
<filter-class>com.yoro.core.web.XsrfTokenRequestFilter</filter-class>
<init-param>
<param-name>splitChar</param-name>
<param-value>@</param-value>
</init-param>
<init-param>
<param-name>addUrls</param-name>
<param-value>/member/forgot_password.html;/login.html</param-value>
</init-param>
<init-param>
<param-name>validUrls</param-name>
<param-value>/member/forgot_password.jhtml;/login.jhtml</param-value>
</init-param>
<init-param>
<param-name>processUrls</param-name>
<param-value>/member/forgot_password.html;/login.html</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>xsrfTokenRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
不介意的可以進入我的店鋪看下增加點人氣
http://qc5z.taobao.com/