小程序登录态管理的方法示例

所谓的登录态其实就是客户端发送请求的时候携带的token(通常叫做令牌),当用户输入账号密码,验证成功之后,服务端生成一个token传递给客户端,客户端在后续的请求中携带这个token,服务器进行校验,校验成功则处理客户端的请求,校验失败则要求客户端重新去登陆。

在web项目中,我们通常使用session来管理这一过程。

客户端首次访问请求的时候,服务端返回一个sessionId作为cookie给客户端,往后客户端每次请求都带上这个cookie与服务端进行通信,当执行完登陆操作以后,服务端将用户数据存入到session中;随后的每次请求,服务端都从cookie中取出sessionId,利用sessionId去查询session,利用session中是否含有用户信息来判断用户是否有登陆。

关于cookie与session的关系,请先看笔者之前的一篇文章:浅谈cookie和session

一.小程序的登录态

要明白小程序跟传统的web项目的不同之处在于它不依托于浏览器,所以它没有cookie,自然无法用session来管理登录态。这给我们的编码造成了不小麻烦。但是其实我们可以通过在请求头中加入键为JESSIONID(或者SESSION),值为sessionId的cookie来模拟这种操作。同时在服务端响应给小程序的时候,若sessionId有发生变化则再回传给客户端。

还有一个要注意的是,小程序也有自己的登录态,那就是session_key的生命周期,session_key是小程序中为了加密数据而提供的一个密钥,具有一定的生命周期。查看小程序官方文档,可以知道它是在服务端调用code2Session获取的。可以通过小程序的wx.checkSession()来校验小程序端的登录态是否过期。

弄清楚了上述两点,我们的要解决的问题包括。

1.校验小程序的登录态

2.校验服务端的登录态,即是否能从session中拿到用户数据。

3.任何一方的登录态过期,都调用登陆的相关代码,注意登陆的相关代码包含小程序端和服务端。后续会说。

4.用户信息如何储存。在web项目里,我们是将用户信息存放在session里,这样在服务端就可以直接用,而借助jsp的某些标签,在jsp页面我们也可以直接从session中拿出用户数据。但现在是小程序,在服务端我们依然可以从session中获取用户数据,但是在客户端,必须等待服务端的回传。这样每次请求都响应用户数据的做法显然不是很合理的,所以我们可以将用户数据保存在微信的缓存里。

5.拦截器问题,在web项目中,我们会在服务端给每个controller写拦截器,拦截器一般是判断登录态,判断成功则执行controller中的代码,失败的话,我们一般会重定向到登陆页面,或者执行完登陆代码后重定向到某个特定页面(微信站中这样做的)。但是这种做法在小程序中是无效的,小程序是动静分离的,我们不可能从服务端去重定向到小程序的特定页面,也不可能从服务端去调用小程序的wx.login()方法。所以,我们把这种拦截校验的发起从服务端移到小程序端。让小程序主动发起这种校验,也就是第二点的检查服务端登录态。

二.小程序登录态的方案

经过上面的分析,我们整理出小程序登录态的方案。

1.在需要用户登录态的页面,首先从缓存中获取用户数据userInfo,若无数据,则跳4

2.调用wx.checkSession()检查小程序端的登录态是否过期,若没过期,跳3,若过期,跳4

3.调用服务端的代码检查session是否过期(即检查服务端的登录态),若没过期则拿到用户数据继续执行后续的操作。若过期,则跳4.

4.登录操作,登录操作分为如下几个步骤。

--a.小程序端调用wx.login()接口得到code。(code只能使用一次)

--b.服务端利用这个code访问code2Session接口得到session_key和open_id,并将session_key和open_id存入到session中。

--c.服务端执行登录操作,主要是通过open_id去数据库中寻找用户数据,若无则新增用户到数据库,若有则取出用户数据。

--d.将用户数据userInfo,session_key,open_id等数据都存放到session中,方便服务端下次拿。

--e.将用户数据userInfo,连同session的sessionId一起响应给小程序端。

--f.小程序端得到用户数据和userInfo后更新缓存中的userInfo(包括JESSIONID的值sessionId)

上述过程可以用微信官方的这张图来表示。

这边的自定义登录态就是sessionId,自定义登录态与session_key,openid关联就是将session_key,openid存入到session中。

下面我们来看具体的代码吧。

1.因为很多页面需要取到用户的数据才能继续操作,所以我们在app.js里面写一个getUseInfo方法,供各子页面调用,方法如下。

//获取用户信息,传递的是一个回调函数,获取到用户信息后执行回调函数,传入的参数是userInfo
 getUserInfo: function (cb) {
 const _this = this ;
 wx.checkSession({
 success: function () {
  let userInfo = wx.getStorageSync( 'userInfo' ); //先从内存中获取userInfo
  if (userInfo.result == 1 ) {
  _this.refreshSession(cb);
  } else {
  _this.userLogin(cb);
  }
 },
 fail: function () {
  _this.userLogin(cb);
 }
 })
 },

上述方法的参数是一个回调函数,不同的页面在获取了userInfo以后传入不同的回调函数,回调函数的参数就是要获取的userInfo。

首先,调用wx.checkSession()方法判定小程序端登录态是否失效,失效的话则去执行userLogin(cb)操作,未失效则从缓存中去拿userInfo数据。在userInfo中,我们主要存放的是userName,userFace等用户数据和SESSION,还有一个标志位result,用于判断userInfo缓存数据是否失效。

然后,如果我们能从缓存中拿到用户数据,就要 检验服务端的登录态是否通过。访问refreshSession(cb)方法。代码如下

 //检查服务端session是否过期
 refreshSession: function (cb) {
 const _this = this ;
 let userInfo = wx.getStorageSync( 'userInfo' );
 wx.request({
 url: _this.domain + _this.api.xcxCheckSessionReq,
 method: 'GET' ,
 header: {
  'Cookie' : 'JSESSIONID=' + userInfo.SESSION + ';SESSION=' + userInfo.SESSION,
 },
 success: function (res) {
  if (res.data == 1) {
  _this.globalData.userInfo = userInfo;
  typeof cb == "function" && cb(_this.globalData.userInfo);
  } else {
  wx.removeStorageSync( 'userInfo' );
  _this.userLogin(cb);
  }
 },
 fail: function () {
  wx.removeStorageSync( 'userInfo' );
  _this.userLogin(cb);
 }
 })
 },

此处,调用服务端的接口来验证服务端的session是否已经过期,服务端的代码如下:

public String xcxCheckSession() {
  Integer result;
  HttpServletRequest req = ServletActionContext.getRequest();
  HttpSession s = req.getSession();
  if (s.getAttribute( "c_userId" )!= null ){
  result=1;
  } else {
  result=0;
  }
  OutPutMsg.outPutMsg(result.toString());
  return null ;
 }

其中OutPutMsg方法就是将结果响应给客户端。

上述代码根据小程序端传过来的JSESSIONID或者SESSION的值,利用servlet的特性,根据这个值去获取session,再判断session中是否有用户信息。从而完成服务端的登录态校验。其实原理跟我们在服务端使用拦截器校验session是否过期是一样的。

若服务端登录态校验失败,则需要清空缓存中的userInfo信息,然后去执行userLogin(cb)方法,进行登录。

2.登录操作涉及到小程序端和服务端,小程序端的代码如下:

 userLogin: function (cb) {
 const _this = this ;
 wx.login({
 success: function (res) {
  //获取code然后去访问服务端登录接口,code主要是为了换openId和session_key。
  if (res.code) {
  wx.request({
  url: _this.domain + _this.api.loginCheckReq,
  method: 'POST' ,
  header: {
  'Content-Type' : _this.globalData.postHeader
  },
  data: {
  jsCode: res.code,
  },
  success: function (res) {
  //登录成功
  if (res.data.result == 1) {
   wx.getUserInfo({
   withCredentials: true ,
   success: function (result) {
   res.data.wechatUserInfo = result.userInfo;
   _this.globalData.userInfo = res.data;
   _this.globalData.userInfo.face = '/uploadFiles/' + res.data.userFace;
   typeof cb == "function" && cb(_this.globalData.userInfo)
   wx.setStorageSync( 'userInfo' , _this.globalData.userInfo); //将用户数据存入内存
   },
   fail: function () {
   _this.globalData.userInfo = res.data;
   _this.globalData.userInfo.face = res.data.prefix + '/uploadFiles/' + res.data.userFace;
   typeof cb == "function" && cb(_this.globalData.userInfo)
   wx.setStorageSync( 'userInfo' , _this.globalData.userInfo);
   }
   })
  }
  }
  })
  }
 }
 })
 },

首先小程序端访问wx.login()接口获取code,然后调用服务端的登录代码。服务端的登录伪代码如下:

public String xcxLogin(){
  Integer result;
  Map<String,Object>map= new HashMap<String, Object>();
  try {
  HttpServletRequest req = ServletActionContext.getRequest();
  String jsCode = req.getParameter( "jsCode" );
  String url = "https://api.weixin.qq.com/sns/jscode2session?appid="
   + ConfigUtil.XCX_APP_ID + "&secret="
   + ConfigUtil.XCX_APP_SECRET + "&js_code=" + jsCode
   + "&grant_type=authorization_code" ;
  String urlDetail = URLConnectionUtil.getUrlDetail(url); //访问小程序接口,获取openId,session_key
  JSONObject jsonObject = JSONObject.fromObject(urlDetail);
  String openId=jsonObject.getString( "openid" );
  String session_key=jsonObject.getString( "session_key" );
  TUser user=getUserByOpenId(openId);
  if (user== null ){
   //新增用户,插入到数据库
   TUser userTmp= new TUser();
   user.setOpenId(openId);
   addUser(userTmp);
   user=userTmp;
  }
  session.put( "user" , user); //将user信息放入session
  session.put( "session_key" , session_key); //将session_key放入session
  map.put( "user" , user); //将user信息响应给小程序端
  map.put( "SESSION" , req.getSession().getId()); //将sessionId响应给小程序端
  result= 1 ; //登录操作成功的标志位
  } catch (Exception e) {
  e.printStackTrace();
  }
  map.put( "result" , result);
  JSONObject resInfo=JsonUtil.mapToJsonObject(map);
  OutPutMsg.outPutMsg(resInfo.toString()); //将数据响应给小程序端
  return null ;
 }

先根据code去拿到openId和session_key,然后从数据库去查询是否有这个openId的客户,没有的话直接执行新增操作,然后将user信息(包含openId)和session_key信息存入session,方便服务端下次直接获取。再把user信息和sessionId回传给小程序端。

小程序端拿到这些信息,就可以把他们缓存起来,以备下次使用啦。

3.最后,凡事需要用户登录才能进入的页面,我们都让他调用getUserInfo(cb),并传入cb回调方法,比如。

 onShow: function () {
 const _this = this ;
 app.getUserInfo( function (userInfo) {
 _this.setData({
  userInfo: userInfo,
 })
 });
 },

三.其他注意点

关于上述代码的userLogin()部分,目前主流的有两种。

1.使用wx.login()静默授权,获取用户的openId(),不要求用户绑定手机号,只在涉及到需要用户手机号的时候才让用户来绑定手机号。只需要在userInfo中预留一个标记用户是否有绑定手机号的字段即可。本文介绍的是采用这种登录方式。

2.必须要用户登录输入手机号及验证码才算登录成功,则将userLogin处的逻辑改为跳转至登录页面。然后服务端的判断逻辑则改为通过手机号和验证码来确认用户是否登录成功。其他部分的逻辑不变,这也是目前比较主流的做法

3:可以简单的理解wx.login()接口是静默授权,它能得到用户的openId;而wx.getUserInfo()需要用户授权,可以获取到用户的头像,昵称等信息。还可以通过wx.getUserInfo()获取到unionId等私密信息,但是必须得在已经调用过wx.login()且登录态尚未过期的前提下。

四.unionId机制

如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。

绑定了开发者帐号的小程序,可以通过下面 4 种途径获取 UnionID。

1.调用接口 wx.getUserInfo,从解密数据中获取 UnionID。注意本接口需要用户授权,请开发者妥善处理用户拒绝授权后的情况。

2.如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号。开发者可以直接通过 wx.login + code2Session 获取到该用户 UnionID,无须用户再次授权。

3.如果开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用。开发者也可以直接通过 wx.login + code2Session 获取到该用户 UnionID ,无须用户再次授权。

4.小程序端调用云函数时,当满足 UnionID 获取条件时可在云函数中通过 cloud.getWXContext 获取 UnionID

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 微信小程序登录态控制深入分析

    微信小程序登录态控制深入分析 最近微信小程序终于开放了个人注册,我当然不能浪费这个炫技的好机会,"菲麦日程"小程序正在全力推进中,尽请期待~~ 在登录态控制中,摸索尝试了小一阵子,特此分享 一.微信建议的登录态控制 说明: 1)小程序内通过wx.login接口获得code 2)将code传入后台,后台对微信服务器发起一个https请求换取openid.session_key 3)后台生成一个自身的3rd_session(以此为key值保持openid和session_key),返回给前

  • 微信小程序中做用户登录与登录态维护的实现详解

    总结 大家都知道,在开发中提供用户登录以及维护用户的登录状态,是一个拥有用户系统的软件应用普遍需要做的事情.像微信这样的一个社交平台,如果做一个小程序应用,我们可能很少会去做一个完全脱离和舍弃连接用户信息的纯工具软件. 让用户登录,标识用户和获取用户信息,以用户为核心提供服务,是大部分小程序都会做的事情.我们今天就来了解下在小程序中,如何做用户登录,以及如何去维护这个登录后的会话(Session)状态.下面来看看详细的介绍: 在微信小程序中,我们大致会涉及到以下三类登录方式: 自有的账号注册和登

  • 小程序登录态管理的方法示例

    所谓的登录态其实就是客户端发送请求的时候携带的token(通常叫做令牌),当用户输入账号密码,验证成功之后,服务端生成一个token传递给客户端,客户端在后续的请求中携带这个token,服务器进行校验,校验成功则处理客户端的请求,校验失败则要求客户端重新去登陆. 在web项目中,我们通常使用session来管理这一过程. 客户端首次访问请求的时候,服务端返回一个sessionId作为cookie给客户端,往后客户端每次请求都带上这个cookie与服务端进行通信,当执行完登陆操作以后,服务端将用户

  • java实现微信小程序登录态维护的示例代码

    相信不少喜欢开发的朋友都已经知道微信小程序是个什么物种了,楼主也是从小程序内测期间就开始关注,并且也写过几个已经上线的微信小程序.但是基本上都是写的纯前端,最近楼主从后端到前端写一个完整的小程序项目,中间碰到了一些问题,楼主会找一些个人觉得有学习价值的点不定时的拿出来跟大家分享,希望对你有一些帮助. 本次就从最基本的微信小程序登录态维护开始吧.小程序官方api文档里面有对登录态的一个完整的解释,并且有相关的代码.想看详情,可以出门右转:https://mp.weixin.qq.com/debug

  • 微信小程序登录态和检验注册过没的app.js写法

    0.可参考的官方页面 获取登录凭证:https://developers.weixin.qq.com/miniprogram/dev/api/wx.login.html 检查登录态是否过期: https://developers.weixin.qq.com/miniprogram/dev/api/wx.checkSession.html 备注:你要明白什么是登录态:这里的登录态是微信小程序自己的登录态,我们可以再自己写个登录页面作为自己的登录态,不过为了用户体验良好我直接以微信登录态做为自己的登

  • Golang通过小程序获取微信openid的方法示例

    为什么要获取小程序的 openid 在开发微信小程序的过程中,小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系.那么这个用户身份标识就是 openid. 小程序获取 openid 的流程 那么小程序获取 openid 的流程具体如下,这里我简化了一下,因为我们只需要获取到 openid 即可,具体可以参考 这里 我们需要在小程序中调用 wx.login() 获取 code 码,然后将这个 code 码发送给后端,后端带着这个 code 码和 app

  • 小程序微信支付功能配置方法示例详解【基于thinkPHP】

    本文实例讲述了小程序微信支付功能配置方法.分享给大家供大家参考,具体如下: ★ 背景 近期进行小程序的开发,毕竟是商城项目的开发,最后牵扯到的微信支付是必要的 个人开发过程中也是遇到各种问题,在此,我根据自己的实际操作,进行了代码的详细配置,以方便小程序新手的快速操作 -  使用语言:PHP             # PHP世界上最好的语言 HaHahahaaha -  使用框架:ThinkPHP 3.2    # 版本有点低而已,没啥大碍 -  测试工具:微信开发者工具    # 其实还挺好

  • 微信小程序获取网络类型的方法示例 原创

    本文实例讲述了微信小程序获取网络类型的方法.分享给大家供大家参考,具体如下: 这里主要演示通过wx.getNetworkType获取当前网络类型的操作方法.代码如下: index.js: Page({ /** * 页面的初始数据 */ data: { netType:'' }, /** * 生命周期函数--监听页面加载 */ onLoad: function () { var that = this; try { wx.getNetworkType({ success: function(res

  • apicloud拉起小程序并传递参数的方法示例

    最近工作中遇到一个需求:App拉起微信小程序.App是用APICloud开发的.查阅APICloud文档发现 端API->开放SDK->wx模块下有launchMiniProgram方法可以实现官方文档 于是在项目中添加wx模块. 官方示例代码: var wx = api.require('wx'); wx.launchMiniProgram({ apiKey: '', // 字符串,微信开放平台获取的appid, 不传则从当前widget的config.xml中读取 miniProgramT

  • 微信小程序对图片进行canvas压缩的方法示例详解

    微信小程序其实自带一个图片压缩的API wx.compressImage,但是这玩意目前感受就是个垃圾.IOS大多数情况下据说还可以,安卓有的时候降低质量压缩后体积反而变大,而且没办法控制其压缩至具体指定的大小,压缩后多大看天意.所以需要使用画布去自己实现一个图片压缩方法. 简单来讲原理就是:找个不显示在页面上的画布画上去,再取出,如果体积还是太大,缩小尺寸后再画,再取,递归下去,直到体积满足要求.(所以限制的越小,图片越大,压缩越久,递归次数越多) 第一步:新建一个zipPic.js文件(名字

  • 在小程序中使用canvas的方法示例

    一直没有怎么使用过canvas,小程序也是之前看过一个视频而已,想要找个例子结合一下两者.所以一个小小的保存图片作为朋友圈相册封面的demo就这么出现了,在这里主要记录一下自己遇到的一些问题以及解决的办法. 截图: 效果图/制作页面/颜色选择 主要是以下几个问题: 1.颜色选择器 2.页面通信 3.组件间通信 4.canvas(生成图片.预览/保存.文字换行) 1.颜色选择器 从小程序的文档中没有找到相应的颜色选择组件,昨天(18.3.13)小程序插件功能上线了,小伙伴们也可以选择插件.写这个d

随机推荐