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 (); privatestaticMap actionUrls=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 { Map data=newHashMap (); data.put("flag",0); data.put("msg","请不要频繁操作!"); writeMessageUtf8(response,data); returnfalse; } /** *当发现一个合法令牌时调用. */ protectedbooleanhandleValidToken(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException { returntrue; } privatevoidwriteMessageUtf8(HttpServletResponseresponse,Map json)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的自定义标签来做。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。