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";
- }
- }
- 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
本人做了一些修改