springMVC中基于token防止表单重复提交方法
本文介绍了springMVC中基于token防止表单重复提交方法,分享给大家,具体如下:
实现思路:
在springmvc配置文件中加入拦截器的配置,拦截两类请求,一类是到页面的,一类是提交表单的。当转到页面的请求到来时,生成token的名字和token值,一份放到Redis缓存中,一份放传给页面表单的隐藏域。(注:这里之所以使用redis缓存,是因为tomcat服务器是集群部署的,要保证token的存储介质是全局线程安全的,而redis是单线程的)
当表单请求提交时,拦截器得到参数中的tokenName和token,然后到缓存中去取token值,如果能匹配上,请求就通过,不能匹配上就不通过。这里的tokenName生成时也是随机的,每次请求都不一样。而从缓存中取token值时,会立即将其删除(删与读是原子的,无线程安全问题)。
实现方式:
TokenInterceptor.Java
packagecom.xxx.www.common.interceptor;
importjava.io.IOException;
importjava.util.HashMap;
importjava.util.Map;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.apache.log4j.Logger;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;
importcom.xxx.cache.redis.IRedisCacheClient;
importcom.xxx.common.utility.JsonUtil;
importcom.xxx.www.common.utils.TokenHelper;
/**
*
*@seeTokenHelper
*/
publicclassTokenInterceptorextendsHandlerInterceptorAdapter
{
privatestaticLoggerlog=Logger.getLogger(TokenInterceptor.class);
privatestaticMapviewUrls=newHashMap();
privatestaticMapactionUrls=newHashMap();
privateObjectclock=newObject();
@Autowired
privateIRedisCacheClientredisCacheClient;
static
{
viewUrls.put("/user/regc/brandregnamecard/","GET");
viewUrls.put("/user/regc/regnamecard/","GET");
actionUrls.put("/user/regc/brandregnamecard/","POST");
actionUrls.put("/user/regc/regnamecard/","POST");
}
{
TokenHelper.setRedisCacheClient(redisCacheClient);
}
/**
*拦截方法,添加or验证token
*/
@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException
{
Stringurl=request.getRequestURI();
Stringmethod=request.getMethod();
if(viewUrls.keySet().contains(url)&&((viewUrls.get(url))==null||viewUrls.get(url).equals(method)))
{
TokenHelper.setToken(request);
returntrue;
}
elseif(actionUrls.keySet().contains(url)&&((actionUrls.get(url))==null||actionUrls.get(url).equals(method)))
{
log.debug("Interceptinginvocationtocheckforvalidtransactiontoken.");
returnhandleToken(request,response,handler);
}
returntrue;
}
protectedbooleanhandleToken(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException
{
synchronized(clock)
{
if(!TokenHelper.validToken(request))
{
System.out.println("未通过验证...");
returnhandleInvalidToken(request,response,handler);
}
}
System.out.println("通过验证...");
returnhandleValidToken(request,response,handler);
}
/**
*当出现一个非法令牌时调用
*/
protectedbooleanhandleInvalidToken(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException
{
Mapdata=newHashMap();
data.put("flag",0);
data.put("msg","请不要频繁操作!");
writeMessageUtf8(response,data);
returnfalse;
}
/**
*当发现一个合法令牌时调用.
*/
protectedbooleanhandleValidToken(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException
{
returntrue;
}
privatevoidwriteMessageUtf8(HttpServletResponseresponse,Mapjson)throwsIOException
{
try
{
response.setCharacterEncoding("UTF-8");
response.getWriter().print(JsonUtil.toJson(json));
}
finally
{
response.getWriter().close();
}
}
}
TokenHelper.java
packagecom.xxx.www.common.utils;
importjava.math.BigInteger;
importjava.util.Map;
importjava.util.Random;
importjavax.servlet.http.HttpServletRequest;
importorg.apache.log4j.Logger;
importcom.xxx.cache.redis.IRedisCacheClient;
/**
*TokenHelper
*
*/
publicclassTokenHelper
{
/**
*保存token值的默认命名空间
*/
publicstaticfinalStringTOKEN_NAMESPACE="xxx.tokens";
/**
*持有token名称的字段名
*/
publicstaticfinalStringTOKEN_NAME_FIELD="xxx.token.name";
privatestaticfinalLoggerLOG=Logger.getLogger(TokenHelper.class);
privatestaticfinalRandomRANDOM=newRandom();
privatestaticIRedisCacheClientredisCacheClient;//缓存调用,代替session,支持分布式
publicstaticvoidsetRedisCacheClient(IRedisCacheClientredisCacheClient)
{
TokenHelper.redisCacheClient=redisCacheClient;
}
/**
*使用随机字串作为token名字保存token
*
*@paramrequest
*@returntoken
*/
publicstaticStringsetToken(HttpServletRequestrequest)
{
returnsetToken(request,generateGUID());
}
/**
*使用给定的字串作为token名字保存token
*
*@paramrequest
*@paramtokenName
*@returntoken
*/
privatestaticStringsetToken(HttpServletRequestrequest,StringtokenName)
{
Stringtoken=generateGUID();
setCacheToken(request,tokenName,token);
returntoken;
}
/**
*保存一个给定名字和值的token
*
*@paramrequest
*@paramtokenName
*@paramtoken
*/
privatestaticvoidsetCacheToken(HttpServletRequestrequest,StringtokenName,Stringtoken)
{
try
{
StringtokenName0=buildTokenCacheAttributeName(tokenName);
redisCacheClient.listLpush(tokenName0,token);
request.setAttribute(TOKEN_NAME_FIELD,tokenName);
request.setAttribute(tokenName,token);
}
catch(IllegalStateExceptione)
{
Stringmsg="ErrorcreatingHttpSessiondueresponseiscommitedtoclient.YoucanusetheCreateSessionInterceptororcreatetheHttpSessionfromyouractionbeforetheresultisrenderedtotheclient:"+e.getMessage();
LOG.error(msg,e);
thrownewIllegalArgumentException(msg);
}
}
/**
*构建一个基于token名字的带有命名空间为前缀的token名字
*
*@paramtokenName
*@returnthenamespaceprefixedsessiontokenname
*/
publicstaticStringbuildTokenCacheAttributeName(StringtokenName)
{
returnTOKEN_NAMESPACE+"."+tokenName;
}
/**
*从请求域中获取给定token名字的token值
*
*@paramtokenName
*@returnthetokenStringornull,ifthetokencouldnotbefound
*/
publicstaticStringgetToken(HttpServletRequestrequest,StringtokenName)
{
if(tokenName==null)
{
returnnull;
}
Mapparams=request.getParameterMap();
String[]tokens=(String[])(String[])params.get(tokenName);
Stringtoken;
if((tokens==null)||(tokens.length<1))
{
LOG.warn("Couldnotfindtokenmappedtotokenname"+tokenName);
returnnull;
}
token=tokens[0];
returntoken;
}
/**
*从请求参数中获取token名字
*
*@returnthetokennamefoundintheparams,ornullifitcouldnotbefound
*/
publicstaticStringgetTokenName(HttpServletRequestrequest)
{
Mapparams=request.getParameterMap();
if(!params.containsKey(TOKEN_NAME_FIELD))
{
LOG.warn("Couldnotfindtokennameinparams.");
returnnull;
}
String[]tokenNames=(String[])params.get(TOKEN_NAME_FIELD);
StringtokenName;
if((tokenNames==null)||(tokenNames.length<1))
{
LOG.warn("Gotanulloremptytokenname.");
returnnull;
}
tokenName=tokenNames[0];
returntokenName;
}
/**
*验证当前请求参数中的token是否合法,如果合法的token出现就会删除它,它不会再次成功合法的token
*
*@return验证结果
*/
publicstaticbooleanvalidToken(HttpServletRequestrequest)
{
StringtokenName=getTokenName(request);
if(tokenName==null)
{
LOG.debug("notokennamefound->Invalidtoken");
returnfalse;
}
Stringtoken=getToken(request,tokenName);
if(token==null)
{
if(LOG.isDebugEnabled())
{
LOG.debug("notokenfoundfortokenname"+tokenName+"->Invalidtoken");
}
returnfalse;
}
StringtokenCacheName=buildTokenCacheAttributeName(tokenName);
StringcacheToken=redisCacheClient.listLpop(tokenCacheName);
if(!token.equals(cacheToken))
{
LOG.warn("xxx.internal.invalid.tokenFormtoken"+token+"doesnotmatchthesessiontoken"+cacheToken+".");
returnfalse;
}
//removethetokensoitwon'tbeusedagain
returntrue;
}
publicstaticStringgenerateGUID()
{
returnnewBigInteger(165,RANDOM).toString(36).toUpperCase();
}
}
spring-mvc.xml
input.jsp在form中加如下内容:
"value="<%=token%>"/> "/>
当前这里也可以用类似于struts2的自定义标签来做。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。