基於小程式開發的前後端分離之登入狀態
公司接了一個小程式的活。本來後臺想用的是session儲存登入狀態。後來發現登入存進去的sessionId和取時候的sessionId不一樣,匯入無法取到登入狀態,百度了一下才知道,原來是小程式端不支援儲存cookie,後來想到了在微信登入授權後,把openId加密(token),當做key,openId當做value儲存到redis當中。然後小程式端的Storage存一個token,每次訪問介面時帶著這個引數訪問。
登入流程
這是小程式官網的圖,介紹的還是挺詳細的.
小程式登入流程圖
該圖中,“小程式”指的就是我們使用小程式框架寫的程式碼部分,“第三方伺服器”一般就是我們自己的後臺服務程式,“微信伺服器”是微信官方的API伺服器。
微信小程式端
微信小程式端的程式碼我就不貼了,說下流程吧:
1.微信授權,帶著code訪問後臺介面。
2.根據返回的token(加密後的openId)儲存到storage,表頭中。
3.每次訪問後臺介面都帶著token,這裡可能會有一個失效問題,後面我會說到。
java後臺端
/** * @param code 微信開放平臺重定向這裡,攜帶的code碼 * @author xph */ @ResponseBody @RequestMapping("/WXServlet") public HashMap<String, Object> WXServlet(String code){ // 根據code請求access_token,獲取唯一標識openId Map<String, Object> map = CodeUtils.get_access_token(code); String openid = map.get("openid").toString(); //加密 String md5openId = StringUtil.MD5(openid); //使用者入庫 HashMap<String, Object> hashMap = userService.wxLogin(openid); //查詢token是否存在 String token = redisDao.queryUserToken(md5openId); if(StringUtils.isNotBlank(token)) { //如果存在,重新生成 redisDao.deleteUserToken(md5openId); } //重新生成openid redisDao.saveUserTokenToRedisDb(md5openId, openid); hashMap.put("token", md5openId); InitialPicDto pic = initialPicService.getPic(); hashMap.put("pic", pic); return hashMap; }
Redis我這裡就不講了,既然要用,就肯定會用。
既然用到了token,那就需要一個攔截器,在每次請求介面之前判斷是否存在此openId,
CustomeInterceptor 攔截器
public class CustomeInterceptor implements HandlerInterceptor { private final Logger logger =LoggerFactory.getLogger(CustomeInterceptor.class); @Autowired private RedisDao redisdao; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); PrintWriter out = null ; logger.info("request請求地址path:"+request.getServletPath()); //從請求頭獲取token String token = request.getHeader(CommonConstants.TOKEN); //查詢是否存在token String openid = redisdao.queryUserToken(token); if(StringUtils.isBlank(openid)) { out =response.getWriter(); JSONObject jsonObject = new JSONObject(); jsonObject.put("code",ErrorCode.LOGIN_ERROR); jsonObject.put("Error", "登入失敗"); out.write(jsonObject.toJSONString()); out.flush(); out.close(); return false; } redisdao.expireEndTime(token); return true; }
注意我上面提到的失效問題,因為我redis存的失效時間為兩個小時,和微信的失效時間保持一致。但是有個問題是小程式端有個方法會自動判斷是否失效,可能使用者兩個小時候內一直在使用,不會失效,但是我redis存的token會失效。這就會導致一個連線問題。
所以我的解決方案是:在每次訪問介面前,判斷是否存在此token,若存在,則重新整理token的失效時間(兩個小時)。
也就是上面的redisdao.expireEndTime(token);
//設定過期時間
public void expireEndTime(String key) {
this.stringRedisTemplate.expire(key, 2, TimeUnit.HOURS);
}
總結流程
1.小程式端發起請求並攜帶主要引數(code)
2.java後臺接到/login請求後,根據code去呼叫微信介面獲取使用者唯一標識openid和sessionKey
3.根據openid查詢mysql資料庫,判斷該使用者是否存在,如果不存在將使用者非敏感資訊和其他初始化資料存入到資料庫中,如果已存在,不操作
4.根據openid查詢redis資料庫,判斷openid對應的skey是否存在,如果存在則刪除原來老的skey以及對應的openid和sessionKey.
5.通過md5生成唯一的skey,用openid做鍵,skey做值,存入到redis中
6.將微信小程式需要的資料封裝到map中,返回給小程式端