1. 程式人生 > >基於小程式開發的前後端分離之登入狀態

基於小程式開發的前後端分離之登入狀態

公司接了一個小程式的活。本來後臺想用的是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中,返回給小程式端