基于小程序开发的前后端分离之登录状态

公司接了一个小程序的活。本来后台想用的是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中,返回给小程序端

猜你喜欢

转载自blog.csdn.net/xuepuhao/article/details/85197886