关键词搜索

源码搜索 ×
×

跨系统如何保持Session存活和Token共享问题

发布2015-07-28浏览13433次

详情内容

WEB端

问题描述

         WMS系统对监管仓进行访问(监管仓内嵌于WMS系统),但是需要登录监管仓系统才能看到引入WMS系统的界面,否则看不了监管仓。这里涉及到一个监管仓访问超时的问题:如果用户一直在WMS上操作,而对监管仓不闻不问,那么一般在30minutes 之后,再次点击监管仓页面就会发现打不开了;同理,如果用户一直停在监管仓操作,那么30minutes之后,回来再次访问WMS的页面的时候,WMS也会自动退出到登录界面。以上问题说明,两个系统使用的不是同一个session时,在同一个系统中操作时间越长,另一个系统session存活的几率就越小,不被用户操作的系统也最容易导致session死亡,大多数时候只能半开半闭式地访问,而不能灵活地游弋于多个系统。要解决session共享的问题才能实现多系统紧密协作。

处理方案

       可以将session用一个公用的数据存储区块存储起来,如:memcache。利用memcache的数据超时来处理session。同时还可以实现单点登录,如果用户通过验证,但是memcache已经存在此session那么就不再允许用户登录系统。

移动端

问题描述

         历史惊人的相似,在Android和IOS上分别访问WMS和监管仓的时候发现不能共享Token。因为此Token是WMS和监管仓都分别作了生成的,登录时也仅仅是对是否超时作了验证,所以在登录的过程中他们是没有问题的,但是访问其他系统页面后切换回来就是在用其他类系统的Token在访问自己的站点,所以导致Token通不过验证。我们并没有通知对方系统自己的Token已经变了,而是自以为是独立地工作。

处理方案

       方法一:同上;

       方法二:每个系统都提供交换Token的接口,改变了就通知对方系统,这样可以确保用户获取到的token永远都是最新的。

我觉得今天这个标题很有意思,值得大家深入去思考,上面的解决方案是余之鄙见,希冀更多处理方案!

Token实现代码

TOken实体

  1. package com.wlyd.wms.service.api.data;
  2. import java.io.Serializable;
  3. public class UserToken implements Serializable
  4. {
  5. private static final long serialVersionUID = -8766321739625153631L;
  6. private String signature;// 签名
  7. private String timestamp;// 时间戳
  8. private String random;// 随机数
  9. private String authorizationCode;//授权码
  10. public String getSignature()
  11. {
  12. return signature;
  13. }
  14. public void setSignature(String signature)
  15. {
  16. this.signature = signature;
  17. }
  18. public String getTimestamp()
  19. {
  20. return timestamp;
  21. }
  22. public void setTimestamp(String timestamp)
  23. {
  24. this.timestamp = timestamp;
  25. }
  26. public String getRandom()
  27. {
  28. return random;
  29. }
  30. public void setRandom(String random)
  31. {
  32. this.random = random;
  33. }
  34. public String getAuthorizationCode()
  35. {
  36. return authorizationCode;
  37. }
  38. public void setAuthorizationCode(String authorizationCode)
  39. {
  40. this.authorizationCode = authorizationCode;
  41. }
  42. @Override
  43. public String toString()
  44. {
  45. return "signature=" + signature + "×tamp=" + timestamp + "&random=" + random+ "&authorizationCode=" + authorizationCode;
  46. }
  47. }
C#与Java DES加密工具

  1. package com.wlyd.wms.util.api;
  2. import java.security.Key;
  3. import java.security.spec.AlgorithmParameterSpec;
  4. import java.util.Calendar;
  5. import java.util.Date;
  6. import java.util.Random;
  7. import javax.crypto.Cipher;
  8. import javax.crypto.SecretKeyFactory;
  9. import javax.crypto.spec.DESKeySpec;
  10. import javax.crypto.spec.IvParameterSpec;
  11. import com.wlyd.wms.service.api.data.UserToken;
  12. import com.wlyd.wms.util.Log;
  13. import Decoder.BASE64Decoder;
  14. import Decoder.BASE64Encoder;
  15. public class JCSharpDESUtil
  16. {
  17. // DES加密的私钥,必须是8位长的字符串
  18. private static final byte[] DESkey = "11111111".getBytes();// 设置密钥
  19. private static final byte[] DESIV = "12345678".getBytes();// 设置向量
  20. static AlgorithmParameterSpec iv = null;// 加密算法的参数接口,IvParameterSpec是它的一个实现
  21. private static Key key = null;
  22. static
  23. {
  24. DESKeySpec keySpec;
  25. try
  26. {
  27. keySpec = new DESKeySpec(DESkey);// 设置密钥参数
  28. iv = new IvParameterSpec(DESIV);// 设置向量
  29. SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 获得密钥工厂
  30. key = keyFactory.generateSecret(keySpec);// 得到密钥对象
  31. } catch (Exception e)
  32. {
  33. Log.getLogger(JCSharpDESUtil.class).error("");
  34. }
  35. }
  36. public static String encrypt(String data)
  37. {
  38. try
  39. {
  40. Cipher enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");// 得到加密对象Cipher
  41. enCipher.init(Cipher.ENCRYPT_MODE, key, iv);// 设置工作模式为加密模式,给出密钥和向量
  42. byte[] pasByte = enCipher.doFinal(data.getBytes("utf-8"));
  43. BASE64Encoder base64Encoder = new BASE64Encoder();
  44. return base64Encoder.encode(pasByte).replace("\n", "").replace("\r", "");// 去掉换行回车符
  45. } catch (Exception e)
  46. {
  47. Log.getLogger(JCSharpDESUtil.class).error("加密异常:" + e.getMessage());
  48. }
  49. return "";
  50. }
  51. public static String decrypt(String data)
  52. {
  53. try
  54. {
  55. Cipher deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
  56. deCipher.init(Cipher.DECRYPT_MODE, key, iv);
  57. BASE64Decoder base64Decoder = new BASE64Decoder();
  58. byte[] pasByte = deCipher.doFinal(base64Decoder.decodeBuffer(data));
  59. return new String(pasByte, "UTF-8");
  60. } catch (Exception e)
  61. {
  62. Log.getLogger(JCSharpDESUtil.class).error("解密异常:" + e.getMessage());
  63. }
  64. return "";
  65. }
  66. public static void main(String[] args) throws Exception
  67. {
  68. String data = "/YG1K/6/mh86b5EbjYdZwrNm+MGzGw7DXXzj1mBM3ssuw2BS2Xan9Yk1mBijRLoujGtD9Wo692KvVUoIvZZY9NdpJvgv76Rn";
  69. System.out.println("加密:" + JCSharpDESUtil.encrypt("boonya"));
  70. System.out.println("解密:" + JCSharpDESUtil.decrypt(JCSharpDESUtil.encrypt("boonya")));
  71. System.out.println("CShap data Length " + (data.getBytes().length % 8));
  72. System.out.println("CShap解密:" + JCSharpDESUtil.decrypt(data));
  73. UserToken encryptedToken = new UserToken();
  74. encryptedToken.setSignature("wmsadmin");
  75. encryptedToken.setTimestamp(Calendar.getInstance().getTimeInMillis() + "");
  76. encryptedToken.setRandom(new Random().nextInt(999999999) + "");
  77. String text = encryptedToken.toString();
  78. System.out.println("明文:" + text);
  79. String encryptData = JCSharpDESUtil.encrypt(text);
  80. System.out.println("加密:" + encryptData);
  81. System.out.println("解密:" + JCSharpDESUtil.decrypt(JCSharpDESUtil.encrypt(text)));
  82. long time = new Date().getTime();
  83. System.out.println(Calendar.getInstance().getTimeInMillis());
  84. System.out.println(new Date(Calendar.getInstance().getTimeInMillis()));
  85. System.out.println(time);
  86. }
  87. }

TOKEN工具类

  1. package com.wlyd.wms.util;
  2. import java.text.SimpleDateFormat;
  3. import java.util.Calendar;
  4. import java.util.Date;
  5. import java.util.Random;
  6. import java.util.concurrent.ConcurrentHashMap;
  7. import com.wlyd.wms.service.api.data.UserToken;
  8. import com.wlyd.wms.util.api.JCSharpDESUtil;
  9. public class TokenUtil
  10. {
  11. private static ConcurrentHashMap<String, UserToken> map = new ConcurrentHashMap<String, UserToken>();
  12. /**
  13. * 生成加密Token
  14. *
  15. * @param username
  16. * @return
  17. */
  18. public static UserToken generateToken(String username)
  19. {
  20. UserToken userToken = new UserToken();
  21. // 设置签名
  22. userToken.setSignature(username);
  23. // 设置时间戳
  24. userToken.setTimestamp(getTimeStamp());
  25. // 设置随机数
  26. userToken.setRandom(getRandom());
  27. // 设置授权码
  28. userToken.setAuthorizationCode(getAuthorizationCode(username));
  29. return userToken;
  30. }
  31. /**
  32. * 判定是否已经登录
  33. *
  34. * @param signature
  35. * @return
  36. */
  37. public static boolean whetherUserHasSignedIn(String signature)
  38. {
  39. if(map.containsKey(signature))
  40. {
  41. return true;
  42. }
  43. return false;
  44. }
  45. /**
  46. * 创建用户Token
  47. *
  48. * @param username
  49. * @return
  50. */
  51. public static String createToken(String username)
  52. {
  53. UserToken userToken = generateToken(username);
  54. // 保存用户Token
  55. String token=encryptUserToken(userToken);
  56. map.put(encryptSignature(userToken.getSignature()), userToken);
  57. return token;
  58. }
  59. /**
  60. * 设置自定义的Token
  61. *
  62. * @param username
  63. * @param userToken
  64. */
  65. public static void setSelfToken(String username,UserToken userToken)
  66. {
  67. map.put(username, userToken);
  68. }
  69. /**
  70. * 【重新生成】更新Token
  71. *
  72. * @param token
  73. * @return
  74. */
  75. public static String reCreateToken(String token)
  76. {
  77. UserToken userToken=TokenUtil.analyseEncryptedUserToken(token);
  78. return createToken(userToken.getSignature());
  79. }
  80. /**
  81. * 加密签名
  82. *
  83. * @param username
  84. * @return
  85. */
  86. public static String encryptSignature(String username)
  87. {
  88. return JCSharpDESUtil.encrypt(username);
  89. }
  90. /**
  91. * 解密签名
  92. *
  93. * @param signature
  94. * @return
  95. */
  96. public static String decryptSignature(String signature)
  97. {
  98. return JCSharpDESUtil.decrypt(signature);
  99. }
  100. /**
  101. * 生成时间戳
  102. * @return
  103. */
  104. public static String getTimeStamp()
  105. {
  106. return Calendar.getInstance().getTimeInMillis() + "";
  107. }
  108. /**
  109. * 生成随机数
  110. *
  111. * @return
  112. */
  113. public static String getRandom()
  114. {
  115. return new Random().nextInt(999999999) + "";
  116. }
  117. /**
  118. * 加密用户Token
  119. *
  120. * @param userToken
  121. * @return
  122. */
  123. public static String encryptUserToken(UserToken userToken)
  124. {
  125. return JCSharpDESUtil.encrypt(userToken.toString());
  126. }
  127. /**
  128. * 解析加密用户Token
  129. *
  130. * @param token
  131. * @return
  132. */
  133. public static UserToken decryptUserToken(String token)
  134. {
  135. String decryptToken=JCSharpDESUtil.decrypt(token);
  136. String[] params = decryptToken.split("&");
  137. // 分析用户提交过来的Token
  138. UserToken userToken = new UserToken();
  139. for (int i = 0, j = params.length; i < j; i++)
  140. {
  141. String[] currentParams = params[i].split("=");
  142. String param = currentParams[0];
  143. switch (param)
  144. {
  145. case "signature":
  146. userToken.setSignature(currentParams[1]);
  147. break;
  148. case "timestamp":
  149. userToken.setTimestamp(currentParams[1]);
  150. break;
  151. case "random":
  152. userToken.setRandom(currentParams[1]);
  153. break;
  154. case "authorizationCode":
  155. userToken.setAuthorizationCode(currentParams[1]);
  156. break;
  157. default:
  158. break;
  159. }
  160. }
  161. return userToken;
  162. }
  163. /**
  164. * 【解密用户Token】分析用户Token
  165. *
  166. * @param token
  167. * @return
  168. */
  169. public static UserToken analyseEncryptedUserToken(String token)
  170. {
  171. UserToken userToken=decryptUserToken( token);
  172. return userToken;
  173. }
  174. /**
  175. * 验证用户Token
  176. *
  177. * @param token
  178. * @param userToken
  179. * @return
  180. */
  181. public static boolean verifyUserToken(String token)
  182. {
  183. // 分析用户需要验证的Token
  184. UserToken userToken=decryptUserToken(token);
  185. // 给用户加密签名
  186. String encryptSignature=encryptSignature(userToken.getSignature());
  187. // 判定是否包含此Token
  188. if(!map.containsKey(encryptSignature))
  189. {
  190. return false;
  191. }
  192. // 获取登录的用户Token
  193. UserToken userTokenIner=(UserToken) map.get(encryptSignature);
  194. // 验证是否存在此用户登录的Token
  195. if (userTokenIner!=null&&(userToken.toString()).equals(userTokenIner.toString()))
  196. {
  197. // 判定时间戳是否过期
  198. long currentTime = Calendar.getInstance().getTimeInMillis();
  199. long timestamp = Long.valueOf(userToken.getTimestamp());
  200. // Token有效时间为30分钟
  201. long verifyTime = 30 * 60 * 1000;
  202. if (currentTime - timestamp > verifyTime)
  203. {
  204. // 移除过期的Token
  205. map.remove(encryptSignature);
  206. return false;
  207. }
  208. return true;
  209. }
  210. return false;
  211. }
  212. /**
  213. * 获取已经签入的用户Token
  214. *
  215. * @return
  216. */
  217. public static UserToken getUserSignedToken(String signature)
  218. {
  219. if(map.containsKey(signature))
  220. {
  221. return map.get(signature);
  222. }
  223. return null;
  224. }
  225. /**
  226. * 根据用户名获取MD5Token
  227. *
  228. * @param username
  229. * @return
  230. */
  231. public static String getAuthorizationCode(String username)
  232. {
  233. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
  234. String date = sdf.format(new Date());
  235. String token = Md5.getPassSalt("username=" + username + "&date=" + date, Md5.getMD5Str(username));
  236. return token;
  237. }
  238. public static void main(String[] args)
  239. {
  240. String token=TokenUtil.createToken("boonya");
  241. System.out.println("token:"+token);
  242. System.out.println("Original token:"+TokenUtil.analyseEncryptedUserToken(token));
  243. boolean result=TokenUtil.verifyUserToken(token);
  244. System.out.println("verify reulst:"+result);
  245. }
  246. /**
  247. * 创建用户Token
  248. *
  249. * @param username
  250. * @return
  251. */
  252. public static String createTokenIOS(String username){
  253. UserToken userToken = generateToken(username);
  254. map.put(encryptSignature(userToken.getSignature()), userToken);
  255. return userToken.toString();
  256. }
  257. public static UserToken decryptUserTokenIOS(String token)
  258. {
  259. String[] params = token.split("&");
  260. // 分析用户提交过来的Token
  261. UserToken userToken = new UserToken();
  262. for (int i = 0, j = params.length; i < j; i++)
  263. {
  264. String[] currentParams = params[i].split("=");
  265. String param = currentParams[0];
  266. switch (param)
  267. {
  268. case "signature":
  269. userToken.setSignature(currentParams[1]);
  270. break;
  271. case "timestamp":
  272. userToken.setTimestamp(currentParams[1]);
  273. break;
  274. case "random":
  275. userToken.setRandom(currentParams[1]);
  276. break;
  277. case "authorizationCode":
  278. userToken.setAuthorizationCode(currentParams[1]);
  279. break;
  280. default:
  281. break;
  282. }
  283. }
  284. return userToken;
  285. }
  286. /**
  287. * 解析token信息非加密
  288. * @MethodName: verifyUserTokenIOS
  289. * @Description:
  290. * @param token
  291. * @return
  292. * @throws
  293. */
  294. public static boolean verifyUserTokenIOS(String token)
  295. {
  296. // 分析用户需要验证的Token
  297. UserToken userToken=decryptUserTokenIOS(token);
  298. // 给用户加密签名
  299. String encryptSignature=encryptSignature(userToken.getSignature());
  300. // 判定是否包含此Token
  301. if(!map.containsKey(encryptSignature))
  302. {
  303. return false;
  304. }
  305. // 获取登录的用户Token
  306. UserToken userTokenIner=(UserToken) map.get(encryptSignature);
  307. // 验证是否存在此用户登录的Token
  308. if (userTokenIner!=null&&(userToken.toString()).equals(userTokenIner.toString()))
  309. {
  310. // 判定时间戳是否过期
  311. long currentTime = Calendar.getInstance().getTimeInMillis();
  312. long timestamp = Long.valueOf(userToken.getTimestamp());
  313. // Token有效时间为30分钟
  314. long verifyTime = 30 * 60 * 1000;
  315. if (currentTime - timestamp > verifyTime)
  316. {
  317. // 移除过期的Token
  318. map.remove(encryptSignature);
  319. return false;
  320. }
  321. return true;
  322. }
  323. return false;
  324. }
  325. }
交换公钥工具类

  1. package com.wlyd.wms.util.api;
  2. import java.util.Map.Entry;
  3. import java.util.concurrent.ConcurrentHashMap;
  4. import com.wlyd.wms.service.api.data.UserToken;
  5. public class UserSecurityManager
  6. {
  7. private static ConcurrentHashMap<String, UserToken> TOKEN = new ConcurrentHashMap<String, UserToken>();
  8. /**
  9. * <k,v>:<sessionId,clientPublicKey>
  10. */
  11. private static ConcurrentHashMap<String, String> CLIENT_PUBLIC = new ConcurrentHashMap<String, String>();
  12. /**
  13. * <k,v>:<sessionId,serverPublicKey>
  14. */
  15. private static ConcurrentHashMap<String, String> SERVER_PUBLIC = new ConcurrentHashMap<String, String>();
  16. /**
  17. * <k,v>:<serverPublicKey,serverPrivateKey>
  18. */
  19. private static ConcurrentHashMap<String, String> SERVER_PUBLIC_PRIVATE = new ConcurrentHashMap<String, String>();
  20. public static String getSessionId(String clientPublicKey)
  21. {
  22. if(CLIENT_PUBLIC.containsValue(clientPublicKey))
  23. {
  24. for (Entry<String, String > entry: CLIENT_PUBLIC.entrySet())
  25. {
  26. if(entry.getValue().equals(clientPublicKey))
  27. {
  28. return entry.getKey();
  29. }
  30. }
  31. }
  32. return null;
  33. }
  34. public static ConcurrentHashMap<String, UserToken> getTOKEN()
  35. {
  36. return TOKEN;
  37. }
  38. public static void rememberUserToken(String key,UserToken token)
  39. {
  40. TOKEN.put(key, token);
  41. }
  42. public static void removeUserToken(String key)
  43. {
  44. if(TOKEN.containsKey(key))
  45. {
  46. TOKEN.remove(key);
  47. }
  48. }
  49. public static void rememberClientPublicKey(String sessionId,String clientPublicKey)
  50. {
  51. CLIENT_PUBLIC.put(sessionId, clientPublicKey);
  52. }
  53. public static String getClientPublicKey(String sessionId)
  54. {
  55. if(CLIENT_PUBLIC.containsKey(sessionId))
  56. {
  57. return CLIENT_PUBLIC.get(sessionId);
  58. }
  59. return null;
  60. }
  61. public static void removeClientPublicKey(String sessionId)
  62. {
  63. if(CLIENT_PUBLIC.containsKey(sessionId))
  64. {
  65. CLIENT_PUBLIC.remove(sessionId);
  66. }
  67. }
  68. public static void rememberServerPublicKey(String sessionId,String serverPublicKey)
  69. {
  70. SERVER_PUBLIC.put(sessionId, serverPublicKey);
  71. }
  72. public static String getServerPublicKey(String sessionId)
  73. {
  74. if(SERVER_PUBLIC.containsKey(sessionId))
  75. {
  76. return SERVER_PUBLIC.get(sessionId);
  77. }
  78. return null;
  79. }
  80. public static String getServerPrivateKey(String sessionId)
  81. {
  82. if(SERVER_PUBLIC.containsKey(sessionId))
  83. {
  84. String serverPublicKey= SERVER_PUBLIC.get(sessionId);
  85. if(SERVER_PUBLIC_PRIVATE.containsKey(serverPublicKey))
  86. {
  87. SERVER_PUBLIC_PRIVATE.get(serverPublicKey);
  88. }
  89. }
  90. return null;
  91. }
  92. public static void removeServerPublicKey(String sessionId)
  93. {
  94. if(SERVER_PUBLIC.containsKey(sessionId))
  95. {
  96. SERVER_PUBLIC.remove(sessionId);
  97. }
  98. }
  99. public static void rememberServerPublicAndPrivateKey(String serverPublicKey,String serverPrivateKey)
  100. {
  101. SERVER_PUBLIC_PRIVATE.put(serverPublicKey, serverPrivateKey);
  102. }
  103. public static void removeServerPublicAndPrivateKey(String serverPublicKey)
  104. {
  105. if(serverPublicKey==null||serverPublicKey.equals("")) return;
  106. if(SERVER_PUBLIC_PRIVATE.containsKey(serverPublicKey))
  107. {
  108. SERVER_PUBLIC_PRIVATE.remove(serverPublicKey);
  109. }
  110. }
  111. }



相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载