Problem Description:
Now the site in the registration step, because the background to deal with a lot of information, resulting in slow response (test machine poor performance is also a factor to slow down), before the front page to submit information, wait for the backend response, at this time if the user
Click the Submit button again, and the background will save multiple copies of the user information. In order to solve this problem, the token idea of struts2 was borrowed, and the token was realized under SPRINGMVC.
Implementation ideas:
In the SPRINGMVC configuration file, the interceptor configuration is added to intercept two types of requests, one to the page and one for the form submission. When a request to the page arrives, the token's name and token value are generated, one in the Redis cache, and one for the hidden field that is placed on the page form. (Note: The Redis cache is used here because the Tomcat server is deployed as a cluster to ensure that the token storage media is global thread safe and Redis is single-threaded)
When the form requests the submission, the interceptor obtains the Tokenname and token in the parameter, then goes to the cache to fetch the token value, if can match, the request passes, cannot match on not to pass. The tokenname generated here is also random, and each request is not the same. When the token value is taken from the cache, it is immediately deleted (delete and read are atomic, wireless security issues).
Implementation method:
Tokeninterceptor.java
Package com.xxx.www.common.interceptor;
Import java.io.IOException;
Import Java.util.HashMap;
Import Java.util.Map;
Import Javax.servlet.http.HttpServletRequest;
Import Javax.servlet.http.HttpServletResponse;
Import Org.apache.log4j.Logger;
Import org.springframework.beans.factory.annotation.Autowired;
Import Org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
Import com.xxx.cache.redis.IRedisCacheClient;
Import Com.xxx.common.utility.JsonUtil;
Import Com.xxx.www.common.utils.TokenHelper; /** * * @see Tokenhelper */public class Tokeninterceptor extends Handlerinterceptoradapter {private static
Logger log = Logger.getlogger (Tokeninterceptor.class);
private static map<string, string> viewurls = new hashmap<string, string> ();
private static map<string, string> actionurls = new hashmap<string, string> ();
Private Object Clock = new Object ();
@Autowired private irediscacheclient rediscacheclient;
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); }/** * Intercept method, add or Verify token */@Override public boolean prehandle (HttpServletRequest request, Http
Servletresponse response, Object handler) throws Exception {String url = Request.getrequesturi ();
String method = Request.getmethod ();
if (Viewurls.keyset (). Contains (URL) && ((Viewurls.get (URL)) = = NULL | | viewurls.get (URL). Equals (method))) {
Tokenhelper.settoken (Request);
return true; } else if (Actionurls.keyset (). Contains (URL) && ((Actionurls.get (URL)) = = NULL | | actionurls.get (URL). Equal S (method))) {Log.debug ("intercepting INVOCation to check for valid transaction token. ");
return Handletoken (Request, response, handler);
} return true; } protected Boolean Handletoken (HttpServletRequest request, httpservletresponse response, Object handler) throws Exception {synchronized (clock) {if (!
Tokenhelper.validtoken (Request)) {System.out.println ("Failed to validate ...");
return Handleinvalidtoken (Request, response, handler);
}} System.out.println ("by verifying ...");
return Handlevalidtoken (Request, response, handler); }/** * Called when a non-Fiat card is present */protected Boolean Handleinvalidtoken (HttpServletRequest request, Httpservle Tresponse response, Object handler) throws Exception {map<string, object> data = new hashmap<string
, object> ();
Data.put ("flag", 0); Data.put ("msg", "Please do not operate frequently.")
");
WriteMessageUtf8 (response, data); return false;
/** * Called when a legitimate token is found. */protected Boolean Handlevalidtoken (HttpServletRequest request, httpservletresponse response, Object handler) throws
Exception {return true;
} private void WriteMessageUtf8 (httpservletresponse response, map<string, object> json) throws IOException
{try {response.setcharacterencoding ("UTF-8");
Response.getwriter (). Print (Jsonutil.tojson (JSON));
} finally {Response.getwriter (). Close ();
}
}
}
Tokenhelper.java
Package com.xxx.www.common.utils;
Import Java.math.BigInteger;
Import Java.util.Map;
Import Java.util.Random;
Import Javax.servlet.http.HttpServletRequest;
Import Org.apache.log4j.Logger;
Import com.xxx.cache.redis.IRedisCacheClient; /** * * * * * */public class Tokenhelper {/** * Save token Value's default namespace */public static final
String token_namespace = "Xxx.tokens";
/** * The name of the field that holds the TOKEN name */public static final String Token_name_field = "Xxx.token.name";
private static final Logger LOG = Logger.getlogger (Tokenhelper.class);
Private static final random random = new random (); private static irediscacheclient rediscacheclient;//cache call, in place of session, supports distributed public static void Setrediscacheclient (
Irediscacheclient rediscacheclient) {tokenhelper.rediscacheclient = rediscacheclient; }/** * Use random string as token name to save token * * @param request * @return Token */public static StringSettoken (HttpServletRequest request) {return Settoken (Request, Generateguid ());
}/** * Use given string as token name to save token * * @param request * @param tokenname * @return Token */private static string Settoken (HttpServletRequest request, String tokenname) {String token = Gener
Ateguid ();
Setcachetoken (Request, tokenname, token);
return token;
}/** * Save a token of a given name and value * * @param request * @param tokenname * @param token * *
private static void Setcachetoken (HttpServletRequest request, string tokenname, string token) {try
{String TOKENNAME0 = Buildtokencacheattributename (tokenname);
Rediscacheclient.listlpush (TOKENNAME0, token);
Request.setattribute (Token_name_field, tokenname);
Request.setattribute (tokenname, token); } catch (IllegalStateException e) {Stringmsg = "Error Creating HttpSession Due response is commited to client. You can use the Createsessioninterceptor or create the HttpSession from your action before the result was rendered to the C
Lient: "+ e.getmessage ();
Log.error (MSG, E);
throw new IllegalArgumentException (msg); }}/** * Build a token name with a namespace prefixed by token name * * @param tokenname * @return The name space
Prefixed Session Token name */public static string Buildtokencacheattributename (String tokenname) {
return Token_namespace + "." + Tokenname; /** * Gets the token value of the given token name from the request domain * * @param tokenname * @return The token String or null if
The token could not being found */public static string GetToken (HttpServletRequest request, string tokenname) {
if (Tokenname = = null) {return null;
Map params = Request.getparametermap (); String[] Tokens = (striNg[]) (string[]) params.get (tokenname);
String token; if ((tokens = = null) | |
(Tokens.length < 1))
{Log.warn ("Could not find token mapped to token name" + tokenname);
return null;
} token = tokens[0];
return token; }/** * Get token name from request parameter * * @return the token name found in the params, or null if it could not B E found */public static String Gettokenname (HttpServletRequest request) {Map params = Request.getpa
Rametermap ();
if (!params.containskey (Token_name_field)) {Log.warn ("Could not the Find TOKEN NAME in params.");
return null;
} string[] Tokennames = (string[]) params.get (Token_name_field);
String Tokenname; if ((Tokennames = = null) | |
(Tokennames.length < 1))
{Log.warn ("Got a null or empty token name.");
return null; } tokenname = Tokennames[0];
return tokenname; }/** * Verifies that the token in the current request parameter is legal, and if a valid token appears it will be deleted, it will not be successfully valid token * * @return Validation Results */Public
Static Boolean Validtoken (HttpServletRequest request) {String Tokenname = gettokenname (request);
if (Tokenname = = null) {Log.debug ("No token name found-Invalid token");
return false;
} String token = GetToken (request, tokenname); if (token = = null) {if (log.isdebugenabled ()) {Log.debug ("No token found F
or token name "+ Tokenname +"-Invalid token ");
} return false;
} String tokencachename = Buildtokencacheattributename (tokenname);
String Cachetoken = Rediscacheclient.listlpop (tokencachename);
if (!token.equals (Cachetoken)) { Log.warn ("Xxx.internal.invalid.token Form token" + token + "does not match the session token" + Cachetoken + ".")
;
return false;
}//Remove the token so it won ' t is used again return true; public static String Generateguid () {return new BigInteger (165, RANDOM). ToString (). toUpperCase ()
;
}
}
Spring-mvc.xml
<!--token blocker--
<bean id= "Tokeninterceptor" class= "Com.xxx.www.common.interceptor.TokenInterceptor" ></bean>
<bean class= " Org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping ">
<property name=" Interceptors ">
<list>
<ref bean=" Tokeninterceptor "/>
</list>
</property >
</bean>
input.jsp Add the following in the form:
<input type= "hidden" name= "<%=request.getattribute (" Xxx.token.name ")%>" value= "<%=token%>"/>
<input type= "hidden" name= "Xxx.token.name" value= "<%=request.getattribute (" Xxx.token.name ")%>"/>
You can also use a custom label similar to the struts2 to do it now.
Another: The company's domain name has been hidden, with XXX replaced.