天天看點

微信小程式授權登入流程

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"
    }
}
           

解釋都在代碼裡面

上一篇: PPT轉Word