Use the cache to implement Session control between the APP and the server interface,
Unlike the traditional B/S Web system, the interface interaction between mobile apps and servers is generally in C/S mode. In this case, if user login is involved, you cannot rely on the Web container to manage sessions as the Web system does, because each request sent by the APP creates a new Session on the server. Some interfaces related to user privacy or financial transactions must confirm the validity of the current user's logon. Such operations cannot be performed if no logon or logon has expired.
I have seen a "lazy" approach, that is, after a user logs on for the first time, the user ID is saved in the local storage, and the interfaces that then interact with the server are identified by the user ID.
This method has two drawbacks:
To sum up, you can use the cache to simulate the Session management mechanism on the server to solve this problem. Of course, this is only a simple and effective solution that I know to solve the Session of APP users. If any of you have other good plans, please leave a message below.
The cache framework used here is Ehcache, http://www.ehcache.org/downloads/, of course, memcachedcan also be used to access its own. The reason why the Ehcache framework is used is that it is lightweight, fast, and easy to integrate. On the other hand, it is also the default CacheProvider in Hibernate, for projects that have already integrated Hibernate, you do not need to add the Ehcache jar package.
With Ehcache, add the corresponding configuration in the Spring configuration file. The configuration information is as follows:
1 <! -- Configure the cache manager factory --> 2 <bean id = "cacheManager" class = "org. springframework. cache. ehcache. ehCacheManagerFactoryBean "> 3 <property name =" configLocation "value =" classpath: ehcache. xml "/> 4 <property name =" shared "value =" true "/> 5 </bean> 6 <! -- Configure the cache factory with the cache name myCache --> 7 <bean id = "ehcache" class = "org. springframework. cache. ehcache. ehCacheFactoryBean "> 8 <property name =" cacheName "value =" myCache "/> 9 <property name =" cacheManager "ref =" cacheManager "/> 10 </bean>
In addition, the Ehcache configuration file ehcache. xml is configured as follows:
1 <? Xml version = "1.0" encoding = "gbk"?> 2 <ehcache xmlns: xsi = "http://www.w3.org/2001/XMLSchema-instance" 3 xsi: noNamespaceSchemaLocation = "ehcache. xsd"> 4 <diskStore path = "java. io. tmpdir"/> 5 6 <! -- Configure a default cache, required --> 7 <defaultCache maxElementsInMemory = "10000" eternal = "false" timeToIdleSeconds = "30" timeToLiveSeconds = "30" overflowToDisk = "false"/> 8 9 <! -- Configure the custom cache maxElementsInMemory: Maximum number of objects allowed to be created in the cache eternal: whether the objects in the cache are permanent. If yes, the timeout setting is ignored and the objects never expire. 10 timeToIdleSeconds: the cache data passivation time, that is, the maximum time interval between two access times before an element disappears, which is only valid when the element is not permanently resident, 11 if the value is 0, it means that the element can be paused for an infinite period of time. TimeToLiveSeconds: The survival time of cached data, that is, the maximum time interval between an element construction and extinction. 12 is valid only when the element is not permanently resident, if the value is 0, it means that the element can pause for an infinite period of time. OverflowToDisk: whether to enable disk cache when the memory is insufficient. MemoryStoreEvictionPolicy: The elimination algorithm after the cache is full. --> 13 <cache name = "myCache" maxElementsInMemory = "10000" eternal = "true" overflowToDisk = "true" memoryStoreEvictionPolicy = "LFU"/> 14 </ehcache>
After configuring Ehcache, you can directly inject cache instances through @ Autowired or @ Resource. The sample code is as follows:
1 @ Component 2 public class Memory {3 @ Autowired 4 private Cache ehcache; // note that the Cache introduced here is net. sf. ehcache. cache 5 6 public void setValue (String key, String value) {7 ehcache. put (new Element (key, value); 8} 9 10 public Object getValue (String key) {11 Element element = ehcache. get (key); 12 return element! = Null? Element. getValue (): null; 13} 14}
After the cache preparation is complete, the next step is to simulate the user Session. The implementation idea is as follows:
To sum up, the APP must log on and obtain tokens from the server for storage. This Token is used to identify the user when accessing the user's privacy-related interfaces. What the server needs to do is intercept user privacy-related interfaces to verify the tokens and logon information. After verification, the tokens are saved to the thread variables, then, you can retrieve the Token in other operations and obtain the current user information from the cache. In this way, the APP does not need to know the user ID. All it receives is an ID that can be changed. The server can know which user to operate based on this ID.
If the Token is variable, the processing details are different and the effects are different.
To ensure that the same user has only one login information in the cache, the server can separately perform MD5 as Seed for the user name after the Token is generated, that is, MD5 (username ). Then, save the Seed as the key and Token as the value to the cache. Even if the Token is changed, the Seed of each user is fixed and can be indexed to the Token through the Seed, use the Token to clear the previous logon information to avoid storing too many invalid logon information in the cache during repeated logon.
The Token-based Session control code is as follows:
1 @ Component 2 public class Memory {3 4 @ Autowired 5 private Cache ehcache; 6 7/** 8 * disable Cache manager 9 */10 @ PreDestroy11 protected void shutdown () {12 if (ehcache! = Null) {13 ehcache. getCacheManager (). shutdown (); 14} 15} 16 17/** 18 * save current Login User information 19*20 * @ param loginUser21 */22 public void saveLoginUser (LoginUser loginUser) {23 // generate seed and token values 24 String seed = MD5Util. getMD5Code (loginUser. getUsername (); 25 String token = TokenProcessor. getInstance (). generateToken (seed, true); 26 // Save the token to 27 loginUser. setToken (token); 28 // clear the previous logon information 29 clearLoginInfoBySee D (seed); 30 // Save the new token and logon information 31 String timeout = getSystemValue (SystemParam. TOKEN_TIMEOUT); 32 int ttiExpiry = NumberUtils. toInt (timeout) * 60; // converts it to 33 ehcache in seconds. put (new Element (seed, token, false, ttiExpiry, 0); 34 ehcache. put (new Element (token, loginUser, false, ttiExpiry, 0 )); 35} 36 37/** 38 * Get the user information in the current thread 39*40 * @ return41 */42 public LoginUser currentLoginUser () {43 Element element = ehcache. g Et (ThreadTokenHolder. getToken (); 44 return element = null? Null: (LoginUser) element. getValue (); 45} 46 47/** 48 * check whether the user logs on to 49*50 * @ param token51 * @ return52 */53 public boolean checkLoginInfo (String token) based on the token) {54 Element element = ehcache. get (token); 55 return element! = Null & (LoginUser) element. getValue ()! = Null; 56} 57 58/** 59 * clear Logon Information 60 */61 public void clearLoginInfo () {62 LoginUser loginUser = currentLoginUser (); 63 if (loginUser! = Null) {64 // generate seed based on the login user name, and then clear the login information 65 String seed = MD5Util. getMD5Code (loginUser. getUsername (); 66 clearLoginInfoBySeed (seed ); 67} 68} 69 70/** 71 * clear Logon Information Based on seed 72*73 * @ param seed74 */75 public void clearLoginInfoBySeed (String seed) {76 // find the corresponding token77 Element = ehcache according to seed. get (seed); 78 if (element! = Null) {79 // Based on the token to clear the previous login information 80 ehcache. remove (seed); 81 ehcache. remove (element. getValue (); 82} 83} 84}
The code of the Token interceptor is as follows:
1 public class TokenInterceptor extends HandlerInterceptorAdapter {2 @ Autowired 3 private Memory memory; 4 5 private List <String> allowList; // list of allowed URLs 6 7 private static final PathMatcher PATH_MATCHER = new AntPathMatcher (); 8 9 @ Override10 public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {11 // checks whether the request URI is allowed. If not, checks the token information 1 of the request. 2 if (! CheckAllowAccess (request. getRequestURI () {13 // check whether the token value of the request is null. 14 String token = getTokenFromRequest (request); 15 response. setContentType (MediaType. APPLICATION_JSON_VALUE); 16 response. setCharacterEncoding ("UTF-8"); 17 response. setHeader ("Cache-Control", "no-cache, must-revalidate"); 18 if (StringUtils. isEmpty (token) {19 response. getWriter (). write ("Token cannot be blank"); 20 response. getWriter (). close (); 21 retu Rn false; 22} 23 if (! Memory. checkLoginInfo (token) {24 response. getWriter (). write ("Session expired, please log on again"); 25 response. getWriter (). close (); 26 return false; 27} 28 ThreadTokenHolder. setToken (token); // Save the current token for the Controller layer to obtain login user information 29} 30 return super. preHandle (request, response, handler ); 31} 32 33/** 34 * check whether the URI allows 35*36 * @ param URI37 * @ return returns the check result 38 */39 private boolean checkAllowAccess (String URI) {40 if (! URI. startsWith ("/") {41 URI = "/" + URI; 42} 43 for (String allow: allowList) {44 if (PATH_MATCHER.match (allow, URI )) {45 return true; 46} 47} 48 return false; 49} 50 51/** 52 * obtain the token value from the request information 53*54 * @ param request55 * @ return token value 56 */57 private String getTokenFromRequest (HttpServletRequest request) {58 // by default, the token value 59 String token = request is obtained from the header. getHeader (Constants. TOKEN); 60 if (StringUtils. isEmpty (token) {61 // obtain the token value from the request information 62 token = request. getParameter (Constants. TOKEN); 63} 64 return token; 65} 66 67 public List <String> getAllowList () {68 return allowList; 69} 70 71 public void setAllowList (List <String> allowList) {72 this. allowList = allowList; 73} 74}
At this point, the validity of the Interface request can be ensured to some extent, so as not to make it easy for others to forge user information. Even if someone else obtains the Token through illegal means, it is only temporary, when the cache expires or the user logs on again, the Token is invalid. If the server interface has higher security requirements, you can switch to the SSL protocol to prevent request information from being stolen.