微信小程序授权登录流程

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html     微信小程序官方API

说明:

  1. 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  2. 调用 code2Session 接口,换取 用户唯一标识 OpenID 和 会话密钥 session_key

之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

注意:

  1. 会话密钥 session_key 是对用户数据进行 加密签名 的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥
  2. 临时登录凭证 code 只能使用一次

这里仅按照官方推荐的规范来

0. 前置条件

一共有三端: 
- 微信小程序客户端 
- 第三方服务器端
- 微信服务器端

1.检测登录是否有效,如果无效则清楚登录信息(wx.checkSession);


2.调用接口获取登录凭证(code)(wx.login);通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。

3. 客户端获得code,并将code传给第三方服务端

微信小程序端调用wx.login,获取登录凭证(code),并调用接口,将code发送到第三方客户端

4. 第三方服务端用code换session_key和openid

小程序端将code传给第三方服务器端,第三方服务器端调用接口,用code换取session_key和openid

5. 第三方服务端生成新的session(3rd_session)

第三方服务器端拿到请求回来的session_key和openid,先留着,不能给客户端;然后用操作系统提供的真正随机数算法生成一个新的session,叫3rd_session

6. 第三方服务端建立对应关系,并存储

将3rd_session作为key,微信服务端返回的session_key和openid作为值,保存起来

7. 第三方服务端将3rd_session发送到客户端

客户端只拿到3rd_session就够了,大人说话小孩别插嘴,小程序不需要知道session_key和openid

8. 正常请求

小程序每次请求都将3rd_session放在请求头里,第三方服务端解析判断合法性,并进行正常的逻辑处理。

下面就封装一个小程序授权登录的组件

目录结构

1.server.js

 //检测登录是否有效,如果无效则清除登录信息
module.exports = {
checkLogs() {
    let utoken = wx.getStorageSync("userInfo").utoken;
    if (typeof utoken == "undefined") {
      return false;
    }
    this.sendRequest({
      url: '',   //  //检测登录是否有效的接口
      data: {
        utoken
      },
      method: 'POST',
      success: res => {
        if (res.data.code != 200) {
          wx.removeStorageSync('userInfo');
        }
      },
      fail: () => {
        wx.removeStorageSync('userInfo');
      }
    })
  },
 //这里使用了iview框架,全局控制handleShow方法,授权登录的显示
login: function() {
    const selector = '#login'
    const pages = getCurrentPages();
    const ctx = pages[pages.length - 1];
    const componentCtx = ctx.selectComponent(selector);
    if (!componentCtx) {
      console.error('无法找到对应的组件,请按文档说明使用组件');
      return null;
    }
    componentCtx.handleShow();
  }
}

app.js

var server = require('./utils/server');
App({
  onLaunch: function() {
    server.checkLogs();//全局调用checkLogs(),检查登录是否失效
  },
  globalData: {}
})

login.wxml

<!--component/login/login.wxml-->
<view class='fixBox' catchtouchmove='touchMove' catchtap='handleHide' wx:if="{{visible}}">
  <form report-submit="true">
    <button type='primary' form-type='submit' open-type="getUserInfo" catchtap='cantchTap' bindgetuserinfo="getUserInfo">一键授权</button>
  </form>
</view> 
<i-message id="message"/>

注意:wx.authorize({scope: "scope.userInfo"}),无法弹出授权窗口,请使用 <button open-type="getUserInfo"/>

login.js

// component/login/login.js
const server = require('../../utils/server.js');
Component({
  properties: {

  },
  data: {
    formid: null,
    visible: false
  },
  methods: {
    handleShow() {
      this.setData({
        visible: true
      })

    },
    handleHide() {
      this.setData({
        visible: false
      })
    },
    touchMove() {
      return false;
    },
    cantchTap() {
      return false;
    },
    getUserInfo(res) {
      if (res.detail.errMsg == 'getUserInfo:ok') {
        let userInfo = {
          ...res.detail.userInfo
        }
        wx.login({
          success: e => {       
            let code = e.code;  //调用wx.login,获取登录凭证(code),并调用接口,将code发送到第三方客户端
            server.sendRequest({
              url: '',       //小程序端将code传给第三方服务器端,第三方服务器端调用接口,用code换取session_key和openid
              data: {
                encryptedData: res.detail.encryptedData,
                iv: res.detail.iv,
                code: code
              },
              method: 'POST',
              success: res => {
                if (res.data.code == 200) {
                  userInfo = {
                    ...userInfo,
                    ...res.data.result
                  }
                  console.log(userInfo);
                  console.log(res.data.result)
                  wx.setStorageSync('userInfo', userInfo);
                  //授权成功
                  this.triggerEvent('login', {
                    status: 1
                  })
                  this.$Message({
                    content: '登录成功',
                    type: "success"
                  })
                  this.handleHide();
                } else {
                  this.triggerEvent('login', {
                    status: 0
                  })
                  this.$Message({
                    content: '登录失败',
                    type: 'error'
                  });
                  this.handleHide();
                }
              }
            })
          }
        })
      } else {
        this.triggerEvent('login', {
          status: 0
        })
        this.$Message({
          content: '登录失败',
          type: 'error'
        });
        this.handleHide();
      }
    },
    $Message(options) {  //把iview框架里的方法抽取出来
      const componentCtx = this.selectComponent("#message");
      componentCtx.handleShow(options);
    }
  }
})

login.json

​
{
  "component": true,
  "usingComponents": {
    "i-message": "/component/iview/message/index"  //引用iview框架里全局提示框
  }
}

​

login.wxss

index.wxml

<view class='head'>
  <block wx:if="{{userInfo}}">
    <image class="avatarImg" src="{{userInfo.avatarUrl}}"></image>
    <view class="nickname">{{userInfo.nickName}}</view>
  </block>
  <block wx:else>
    <image class="avatarImg" bindtap="login" src="/images/avatar.png"></image>
    <view class="nickname" bindtap="login">未登录</view>
  </block>
</view>
<view class="order">
  <view class="list orderTitle">
    <view class="listTitle">我的订单</view>
    <view class="readMore" bindtap='seeMore'>
      <text>查看更多</text>
      <image class="toRight" src="/images/toRight.png"></image>
    </view>
  </view>
</view>
<login id="login" bind:login="onLogin"></login>

index.js

const server = require('../../utils/server.js');
Page({
  data: {
    useInfo: null
  },
  onLoad: function(options) {},
  onShow: function() {

  },
  onLogin(res) {  // 授权成功的回调,根据子组件传过来的status
    if (res.detail.status == 1) {
      let userInfo = wx.getStorageSync('userInfo');
      this.setData({
        userInfo
      })
    }
  },
  navigateTo(option) {  // 封装一个具有判断是否授权登录的跳转方法
    if (wx.getStorageSync("userInfo")) {
      wx.navigateTo(option);
    } else {
      server.login();
    }
  },
  seeMore() {
    this.navigateTo({
      url: '/pages/order/orderList/orderList'
    })
  },

})

index.wxss

page {
  background-color: #eee;
}

.head {
  width: 100%;
  height: 250rpx;
  background-color: #fff;
  box-sizing: border-box;
  padding: 0 20rpx;
  border-top: 1rpx solid #eee;
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

.avatarImg {
  width: 150rpx;
  height: 150rpx;
  border-radius: 50%;
}

.nickname {
  display: inline-block;
  font-size: 40rpx;
  font-weight: 600;
  color: #212121;
  margin-left: 20rpx;
}

.order, .tool {
  margin-top: 20rpx;
  background-color: #fff;
}

.list {
  border-bottom: 2rpx solid #eee;
  height: 100rpx;
  box-sizing: border-box;
  padding: 0 20rpx;
}

.orderTitle {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.listTitle {
  color: #232323;
  font-size: 35rpx;
}

.readMore {
  line-height: 100rpx;
  font-size: 30rpx;
  color: #989898;
  display: flex;
  align-items: center;
}

.toRight {
  width: 28rpx;
  height: 28rpx;
  margin-left: 20rpx;
}

index.json

{
    "usingComponents": {
        "login": "/component/login/login",
        "i-message": "/component/iview/message/index"
    }
}

解释都在代码里面
 

猜你喜欢

转载自blog.csdn.net/dwb123456123456/article/details/84251932