spring mvc基于token防止重复提交验证
实现思路:
在springmvc配置文件中加入拦截器的配置,拦截两类请求,一类是到页面的,一类是提交表单的。当转到页面的请求到来时,生成token的名字和token值,放入session,在页面表单的隐藏域显示。
当表单请求提交时,拦截器得到参数中的tokenName和token,然后到session中去取token值,如果能匹配上,请求就通过,不能匹配上就不通过。这里的token生成时也是随机的,每次请求都不一样。而从session中取token值时,会立即将其删除(删与读是原子的,无线程安全问题)。
一、首先创建一个token处理类 ,这里的类名叫 TokenHandler
package com.base.utils;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
/**
* 创建时间:2017年4月5日 下午5:56:31
* 项目名称:tra02
*
* @author hc
* @version 1.0
* 文件名称:TokenHandler.java
* 类说明:
*/
public class TokenHandler {
private static Logger logger = Logger.getLogger(TokenHandler.class);
static Map<String, String> springmvc_token = null;
// 生成一个唯一值的token
@SuppressWarnings("unchecked")
public synchronized static String generateGUID(HttpSession session) {
String token = "";
try {
Object obj = session.getAttribute("SPRINGMVC.TOKEN");
if (obj != null)
springmvc_token = (Map<String, String>) session.getAttribute("SPRINGMVC.TOKEN");
else
springmvc_token = new HashMap<String, String>();
token = new BigInteger(165, new Random()).toString(36).toUpperCase();
springmvc_token.put(Constants.DEFAULT_TOKEN_NAME + "." + token, token);
session.setAttribute("SPRINGMVC.TOKEN", springmvc_token);
Constants.TOKEN_VALUE = token;
// System.out.println(session.getAttribute("SPRINGMVC.TOKEN"));
} catch (IllegalStateException e) {
logger.error("generateGUID() mothod find bug,by token session...");
}
return token;
}
// 验证表单token值和session中的token值是否一致
@SuppressWarnings("unchecked")
public static boolean validToken(HttpServletRequest request) {
String inputToken = getInputToken(request);
if (inputToken == null) {
logger.warn("令牌是不是有效的。inputtoken是空的");
return false;
}
HttpSession session = request.getSession();
Map<String, String> tokenMap = (Map<String, String>) session.getAttribute("SPRINGMVC.TOKEN");
if (tokenMap == null || tokenMap.size() < 1) {
logger.warn("token is not valid!sessionToken is NULL");
return false;
}
String sessionToken = tokenMap.get(Constants.DEFAULT_TOKEN_NAME + "." + inputToken);
if (!inputToken.equals(sessionToken)) {
logger.warn("token is not valid!inputToken='" + inputToken + "',sessionToken = '" + sessionToken + "'");
return false;
}
tokenMap.remove(Constants.DEFAULT_TOKEN_NAME + "." + inputToken);
session.setAttribute("SPRINGMVC.TOKEN", tokenMap);
return true;
}
// 获取表单中token值
@SuppressWarnings("unchecked")
public static String getInputToken(HttpServletRequest request) {
Map params = request.getParameterMap();
System.out.println(request.getSession().getAttribute("SPRINGMVC.TOKEN"));
if (!params.containsKey(Constants.DEFAULT_TOKEN_NAME)) {
logger.warn("无法找到令牌名称参数。");
return null;
}
String[] tokens = (String[]) (String[]) params.get(Constants.DEFAULT_TOKEN_NAME);
if ((tokens == null) || (tokens.length < 1)) {
logger.warn("得到空或空的令牌名称。");
return null;
}
String temp = tokens[0];
String inputtoken = temp.substring(temp.indexOf("=") + 1, (temp.length()) - 1);
return inputtoken;
}
}
二、建立常量配置类Constabts
package com。base.utils;
/**
* 创建时间:2017年4月5日 下午5:57:58
* 项目名称:traffic02
*
* @author hc
* @version 1.0
* 文件名称:Constants.java
* 类说明:
*/
public class Constants {
public static String DEFAULT_TOKEN_MSG_JSP = "add.jsp";
public static String TOKEN_VALUE;
public static String DEFAULT_TOKEN_NAME = "springMVC.token";
}
三、前端页面 主要是隐藏域
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<div align="center">
<form action="user/add" method="post" >
<input type="hidden" name="springMVC.token" value="<%=session.getAttribute("SPRINGMVC.TOKEN")%>" >
名字:<input type="text" name="username">
密码:<input type="password" name="password"/>
<input type="submit" value="提交">
</form>
</div>
</body>
</html>
四、controller不用多写什么,能跳转举行
package com.traffic.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 创建时间:2017年4月5日 下午1:43:00
* 项目名称:tra
* @author hc
* @version 1.0
* 文件名称:UserController.java
* 类说明:
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/toadd")
public String toadd(Model model){
return "forward:/add.jsp";
}
@RequestMapping("/add")
public String add(String username,String password,Model model){
System.out.println("suc");
return "forward:/suc.jsp";
}
}
五、重点是拦截器
一个生成token,一个验证token。
public class TokenHandlerInterceptor implements HandlerInterceptor{
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
}
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object arg2, ModelAndView arg3) throws Exception {
TokenHandler.generateGUID(request.getSession());
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object arg2) throws Exception {
return true;
}
}
/**
* @Title
* @author
* @date
*/
public class TokenValidInterceptor implements HandlerInterceptor{
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object arg2, Exception arg3)
throws Exception {
}
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3) throws Exception {
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object arg2) throws Exception {
if(!TokenHandler.validToken(request)){
response.sendRedirect(Constants.DEFAULT_TOKEN_MSG_JSP);
return false;
}
return true;
}
}
六、当然 springmvc要配置拦截器
<mvc:interceptor>
<mvc:mapping path="/index.do" />-->这个请求返回的是你有token的页面
<bean class="com.dengyang.interceptor.TokenHandlerInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/indexSubmit.do" />-->这个是提交请求
<bean class="com.dengyang.interceptor.TokenValidInterceptor" />
</mvc:interceptor>
七、本方案如果不足之处,请大神们指教;
声明:本方案源于
http://www.oschina.net/code/snippet_100825_21906
本人做了一些修改