日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

微信小程序授權(quán)登錄三種實現(xiàn)方式

作者:天邊月_ 更新時間: 2023-04-19 編程語言

方式一:小程序授權(quán)登錄

通過wx.login獲取 臨時登錄憑證code,向后端換取token。 可以做到無感登錄。

時序圖:

在這里插入圖片描述

說明:

1、客戶端調(diào)用 wx.login() 獲取 臨時登錄憑證code,通過 wx.request() 發(fā)起網(wǎng)絡(luò)請求,將 code 傳給服務(wù)端
2、服務(wù)端使用 code + appid + appsecret 向微信換取 (調(diào)用 auth.code2Session 接口)用戶唯一標(biāo)識openid 和 會話密鑰session_key
3、服務(wù)端自定義 登錄狀態(tài)token(與openid、session_key關(guān)聯(lián))返回客戶端
4、客戶端將 登錄狀態(tài)token 存入 緩存storage(推薦使用 wx.setStorageSync(‘key’, ‘value’) 同步存儲)
5、客戶端wx.request() 發(fā)起請求時,攜帶 登錄狀態(tài)token (推薦使用 wx.getStorageSync(‘key’) 同步獲取)
6、服務(wù)端通過 登錄狀態(tài)token 查詢到對應(yīng) openid 和 session_key
7、驗證成功后,返回業(yè)務(wù)數(shù)據(jù)給客戶端

注意:

1、會話密鑰session_key 是對?戶數(shù)據(jù)進(jìn)?加密簽名的密鑰。為了應(yīng)??身的數(shù)據(jù)安全,開發(fā)者服務(wù)器不應(yīng)該把會話密鑰下發(fā)到?程序,也不應(yīng)該對外提供這個密鑰。
2、臨時登錄憑證code 只能使??次

code(以uni-app框架為例):

新建http.js,封裝登錄方法

// baseurl
let baseUrl = 'https://test.com.cn'

// 請求封裝
async function _request(url, method, data = {}) {
  let res = await requestPromise(url, method, data)
  if (res.code == 200) {
    return Promise.resolve(res)
  } else if (res.code == 401) { // 無感刷新  
    return await login(url, method, data)
  } else {
    return Promise.reject(res)
  }
}

// 登錄
async function login(url, method, data) {
  let openIdUrl = new String()
  // #ifdef MP-WEIXIN
  openIdUrl = '微信登錄接口地址'
  //#endif
  //#ifdef MP-ALIPAY
  openIdUrl = '支付寶登錄接口地址'
  //#endif

  let res = await requestPromise(openIdUrl, 'POST', { code: await _getAppCode(), source: 'MP' })
  if (res.code == 200) {
    // 將token,userid存入緩存
    uni.setStorageSync('token', res.data.token)
    uni.setStorageSync('userId', res.data.userId)

    // 再次發(fā)起請求
    return await _request(url, method, data)

  } else {
    return Promise.reject(res)
  }
}

// 發(fā)送request請求
function requestPromise(url, method, data = {}) {
  return new Promise((resolve, reject) => {
    uni.request({
      header: {
        'Content-Type': 'application/json;charset=UTF-8',
        'X-Token': uni.getStorageSync('token') || new String(),
        'X-UserId': uni.getStorageSync('userId') || new String()
      },
      url: `${baseUrl}${url}`,
      method: method,
      data: data,
      success: result => {
        resolve(result)
      },
      fail: error => {
        reject(error)
      },
    })
  })
}

// 獲取臨時登錄憑證code
function _getAppCode() {
  return new Promise((resolve, reject) => {
    // #ifdef MP-WEIXIN
    uni.login({
      provider: 'weixin',
      success(res) {
        resolve(res.code)
      },
      fail(err) {
        reject(err)
      }
    })
    // #endif
    // #ifdef MP-ALIPAY
    // 系統(tǒng)建議使用支付寶原生寫法
    my.getAuthCode({
      scopes: 'auth_base',
      success(res) {
        resolve(res.authCode)
      },
      fail(err) {
        reject(err)
      }
    })
    // #endif
  })
}


module.exports = {
  $get: function(url, data, onSuccess, onError) {
    _request(url, 'GET', data).then(res => {
      onSuccess && onSuccess(res)
    }).catch(err => {
      onError && onError(err)
    })
  },
  $put: function(url, data, onSuccess, onError) {
    _request(url, 'PUT', data).then(res => {
      onSuccess && onSuccess(res)
    }).catch(err => {
      onError && onError(err)
    })
  },
  $post: function(url, data, onSuccess, onError) {
    _request(url, 'POST', data).then(res => {
      onSuccess && onSuccess(res)
    }).catch(err => {
      onError && onError(err)
    })
  },
  $delete: function(url, data, onSuccess, onError) {
    _request(url, 'DELETE', data).then(res => {
      onSuccess && onSuccess(res)
    }).catch(err => {
      onError && onError(err)
    })
  },
  baseUrl: baseUrl
}

新建api.js,對接口進(jìn)行封裝

import https from '../utils/https.js'

export function test(params) {
	return new Promise((resolve, reject) => {
		https.$get("/api", params, res => {
			return resolve(res)
		}, err => {
			return reject(err)
		})
	})
}

方式二:手機(jī)號授權(quán)登錄

過button按鈕的bindgetphonenumber事件,彈出手機(jī)號授權(quán),獲取到加密數(shù)據(jù)后,向后端換取token。

說明:

1、過button按鈕的bindgetphonenumber事件獲取手機(jī)號加密數(shù)據(jù),按鈕需要設(shè)置open-type=“getPhoneNumber”
2、調(diào)用 wx.login() 獲取 臨時登錄憑證code
3、將加密數(shù)據(jù)(encryptedData、iv、signature、rawData)和 臨時登錄憑證code傳給服務(wù)端
4、服務(wù)端使用 code + appid + appsecret 向微信換取 (調(diào)用 auth.code2Session 接口)用戶唯一標(biāo)識openid 和 會話密鑰session_key
5、服務(wù)端根據(jù)session_key,appid ,encryptedData,iv解密手機(jī)號
6、服務(wù)端自定義 登錄狀態(tài)token(與openid、session_key關(guān)聯(lián))返回客戶端
7、客戶端將 登錄狀態(tài)token 存入 緩存storage(推薦使用 wx.setStorageSync(‘key’, ‘value’) 同步存儲)
8、客戶端wx.request() 發(fā)起請求時,攜帶 登錄狀態(tài)token (推薦使用 wx.getStorageSync(‘key’) 同步獲取)
9、服務(wù)端通過 登錄狀態(tài)token 查詢到對應(yīng) openid 和 session_key
10、驗證成功后,返回業(yè)務(wù)數(shù)據(jù)給客戶端

注意:

在回調(diào)中調(diào)? wx.login 登錄,可能會刷新登錄態(tài)。此時服務(wù)器使? code 換取的sessionKey 不是加密時使?的 sessionKey,導(dǎo)致解密失敗。建議開發(fā)者提前進(jìn)? login;或者在回調(diào)中先使? checkSession 進(jìn)?登錄態(tài)檢查,避免 login刷新登錄態(tài)。

也就是說在觸發(fā)getPhoneNumber方法(用戶點(diǎn)擊button)之前,就需要獲取最新code。

code(以uni-app框架為例):

新建wxLogin.vue

<template>
  <view class="wx-login">
    <!-- #ifdef MP-WEIXIN -->
    <u-button type="primary" text="微信用戶一鍵登錄" open-type="getPhoneNumber" :plain="true" @getphonenumber="getUserPhoneNumber"></u-button>
    <!-- #endif -->
    <!-- #ifdef MP-ALIPAY -->
    <button open-type="getPhoneNumber" :plain="true"@getphonenumber="getUserPhoneNumber" scope='userInfo'>支付寶用戶一鍵登錄</button>
    <!-- #endif -->
  </view>
</template>

<script>
  import { mapActions } from 'vuex'
  export default {
    async created() {
      this.code = await this.getAppCode()
    },
    data() {
      return {
        // 用戶憑證
        code: new String()
      }
    },
    methods: {
      ...mapActions(['Login']),
      // 微信用戶手機(jī)號登錄
      getUserPhoneNumber(event) {
        if(event.detail.errMsg !== 'getPhoneNumber:ok') return
        uni.showToast({
          title: '登錄中',
          icon: 'loading',
          mask: true
        })
        event.detail.code = this.code
        this.Login({ userInfo: event.detail }).then(async ({ code, msg }) => {
          if (code == 200) { // 登錄成功,跳轉(zhuǎn)首頁
            uni.reLaunch({ url: '/pages_home/home/index' })
          }  else {
            this.code = await this.getAppCode()
            uni.showToast({
              icon: 'none',
              title: msg,
              duration: 2000
            })
          }
        })
      },
      // 獲取code
      getAppCode() {
        return new Promise((resolve, reject) => {
          // #ifdef MP-WEIXIN
          uni.login({
            provider: 'weixin',
            success(res) {
              resolve(res.code)
            },
            fail(err) {
              reject(err)
            }
          })
          // #endif
          // #ifdef MP-ALIPAY
          my.getAuthCode({
            scopes: 'auth_base',
            success(res) {
              resolve(res.authCode)
            },
            fail(err) {
              reject(err)
            }
          })
          // #endif
        })
      },  
    }
  }
</script>

<style lang="less" scoped>
  .wx-login {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    padding: 0 32rpx 60rpx;

    button {
      font-size: 28rpx;
      font-weight: 400;
    }
  }
</style>

新建store/index.js 封裝登錄邏輯

import Vue from "vue"
import Vuex from 'vuex'
import { loginByMobile } from '@/api/login.js'
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    token: new String(),
    userId: new String(),
    userInfo: new Object(),
  },
  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_USER: (state, userInfo) => {
      state.userInfo = userInfo
    },
    SET_USERID: (state, userId) => {
      state.userId = userId
    },
  },
  actions: {
    // 登錄
    Login({ commit }, { userInfo }) {
      // 微信手機(jī)號登錄
      userInfo.type = 'WX_MP'
      delete userInfo.errMsg
      return new Promise((resolve, reject) => {
        loginByMobile(userInfo).then(response => {
          if (response.code == 200) {
            uni.setStorageSync('USER_ID', response.data.userId)
            uni.setStorageSync('ACCESS_TOKEN', response.data.token)
            uni.setStorageSync('userInfo', response.data)
            commit('SET_USERID', response.data.userId)
            commit('SET_TOKEN', response.data.token)
            commit('SET_USER', response.data)
          }
          resolve(response)
        }).catch(error => {
          reject(error)
        })
      })
    },
    // 登出
    Logout({ commit, state }) {
      return new Promise((resolve) => {
        commit('SET_USERID', new String())
        commit('SET_TOKEN', new String())
        commit('SET_USER', new Object())
        uni.removeStorageSync('USER_ID')
        uni.removeStorageSync('ACCESS_TOKEN')
        uni.removeStorageSync('userInfo')
        resolve('200')
      })
    },
  },
})

export default store

main.js中引入vuex 并且掛載到vue實例上

//引入vuex 并且掛載到vue實例上
import store from "./store/index.js"
Vue.prototype.$store = store

request.js封裝

import env from './env.js'

// http
export const request = (url, method, data = {}) => {
  return new Promise((resolve, reject) => {
    uni.request({
      header: {
        'Content-Type': 'application/json;charset=UTF-8',
        'X-Token': uni.getStorageSync('ACCESS_TOKEN'),
        'X-UserId': uni.getStorageSync('USER_ID'),
        'satoken': uni.getStorageSync('userInfo').satoken
      },
      url: `${env.root}${url}`,
      method: method,
      data: data,
      success: res => {
        if (res.data.code == 401 || res.data.message == '請先登錄') {
          // token過期  重新登錄
          uni.showToast({
            icon: 'none',
            title: '登錄超時,請重新登錄',
            duration: 2000
          })
          uni.removeStorageSync('ACCESS_TOKEN')
          uni.removeStorageSync('USER_ID')
          uni.removeStorageSync('userInfo')
          // 跳轉(zhuǎn)登錄
          uni.redirectTo({ url: '/pages/login/index' })
          reject(res.data)
        } else { // 返回內(nèi)容
          resolve(res.data)
        }
      },
      error: err => {
        console.error("請求失敗", err)
        reject(err)
      },
      complete: () => {},
    })
  })
}

// upload 
export const upload = (url, filePath, name, formData = {}) => {
  return new Promise((resolve, reject) => {
    uni.uploadFile({
      header: {
        'Content-Type': 'application/json;charset=UTF-8',
        'X-Token': uni.getStorageSync('ACCESS_TOKEN'),
        'X-UserId': uni.getStorageSync('USER_ID'),
        'satoken': uni.getStorageSync('userInfo').satoken
      },
      url: `${env.root}${url}`,
      filePath: filePath,
      name: name,
      formData: formData,
      success: res => {
        if (res.data.code == 401 || res.data.message == '請先登錄') {
          // token過期  重新登錄
          uni.showToast({
            icon: 'none',
            title: '登錄超時,請重新登錄',
            duration: 2000
          })
          uni.removeStorageSync('ACCESS_TOKEN')
          uni.removeStorageSync('USER_ID')
          uni.removeStorageSync('userInfo')
          // 跳轉(zhuǎn)登錄
          uni.redirectTo({ url: '/pages/login/index' })
          reject(res.data)
        } else { // 返回內(nèi)容
          resolve(JSON.parse(res.data))
        }
      },
      error: err => {
        console.error("請求失敗", err)
        reject(err)
      },
      complete: () => {},
    })
  })
}

方式三:用戶信息授權(quán)登錄

通過button按鈕的click事件,調(diào)用 wx.getUserProfile() 彈出授權(quán)框,獲取到用戶加密數(shù)據(jù)后,向后端換取token。

說明:

1、通過 wx.getUserProfile() 獲取用戶信息,此方法需要通過button按鈕的click事件觸發(fā)
2、調(diào)用 wx.login() 獲取 臨時登錄憑證code
3、將加密數(shù)據(jù)(encryptedData、iv)和 臨時登錄憑證code傳給服務(wù)端
4、服務(wù)端使用 code + appid + appsecret 向微信換取 (調(diào)用 auth.code2Session 接口)用戶唯一標(biāo)識openid 和 會話密鑰session_key
5、服務(wù)端自定義 登錄狀態(tài)token(與openid、session_key關(guān)聯(lián))返回客戶端,同時返回用戶信息
6、客戶端將 登錄狀態(tài)token 存入 緩存storage(推薦使用 wx.setStorageSync(‘key’, ‘value’) 同步存儲)
7、客戶端wx.request() 發(fā)起請求時,攜帶 登錄狀態(tài)token (推薦使用 wx.getStorageSync(‘key’) 同步獲取)
8、服務(wù)端通過 登錄狀態(tài)token 查詢到對應(yīng) openid 和 session_key
9、驗證成功后,返回業(yè)務(wù)數(shù)據(jù)給客戶端

注意:

在回調(diào)中調(diào)? wx.login 登錄,可能會刷新登錄態(tài)。此時服務(wù)器使? code 換取的sessionKey 不是加密時使?的 sessionKey,導(dǎo)致解密失敗。建議開發(fā)者提前進(jìn)? login;或者在回調(diào)中先使? checkSession 進(jìn)?登錄態(tài)檢查,避免 login刷新登錄態(tài)。

code(以uni-app框架為例):

<template>
	<view class="content">
		<image src="logo.png"></image>
		<view class="title">申請獲取以下權(quán)限</view>
		<text class="msg">獲取你的公開信息(昵稱、頭像、地區(qū)等)</text>
		<!-- #ifdef MP-WEIXIN -->
		<button class="btn" @click="wxgetUserInfo">授權(quán)登錄</button>
		<!-- #endif -->
		<!-- #ifdef MP-ALIPAY -->
		<button class="btn" open-type="getAuthorize" @getAuthorize="alipaygetUserInfo" @error="onAuthError" scope='userInfo'>授權(quán)登錄</button>
		<!-- #endif -->
	</view>
</template>

<script>
	import { loginByWx, loginByAlipay } from '@/api/user.js'
	export default {
		data() {
			return {
				code: new String()
			}
		},
		async onLoad() {
			this.code = await this.getAppCode()
		},
		methods: {
			// 獲取微信用戶信息
			async wxgetUserInfo() {
				try {
					// 微信登錄
					// #ifdef MP-WEIXIN
					let userData = await this._getwxUserData()
					// 調(diào)用后臺接口登錄
					let loginRes = await this.appLogin(userData)
					// savecache
					uni.setStorageSync('isLogin', true)
					uni.setStorageSync('userInfo', {
						headImg: loginRes.headImg,
						userName: loginRes.userName
					});
					uni.setStorageSync('token', loginRes.token)
					uni.setStorageSync('userId', loginRes.userId)
					uni.navigateBack({
						delta: 1
					});
					// #endif					
				} catch(err) {
					this.onAuthError()
				}
			},
			// 支付寶用戶登錄
			async alipaygetUserInfo() {
				try {
					// 支付寶登錄
					// #ifdef MP-ALIPAY
					let userData = await this._getalipayUserData()
					// 調(diào)用后臺接口登錄
					let loginRes = await this.appLogin(userData)
					loginRes.userName = userData.nickName
					loginRes.headImg = userData.avatar
					// savecache
					uni.setStorageSync('isLogin', true)
					uni.setStorageSync('userInfo', {
						headImg: loginRes.headImg,
						userName: loginRes.userName
					})
					uni.setStorageSync('token', loginRes.token)
					uni.setStorageSync('userId', loginRes.userId)
					uni.navigateBack({
						delta: 1
					})
					// #endif					
				} catch(err) {
					this.onAuthError()
				}
			},
			// 授權(quán)失敗
			onAuthError() {
				uni.showToast({
					title: '授權(quán)失敗,請確認(rèn)授權(quán)已開啟',
					mask: true,
					icon: 'none'
				})
			},
			// 獲取支付寶用戶加密數(shù)據(jù)
			_getalipayUserData() {
				return new Promise((resolve, reject) => {
					my.getOpenUserInfo({
						success: res => {
							let userInfo = JSON.parse(res.response).response
							resolve(userInfo)
						},
						fail: err => {
							reject(err)
						}
					})
				})
			},
			// 獲取微信用戶加密數(shù)據(jù)
			_getwxUserData() {
				// 用戶信息接口調(diào)整,使用uni.getUserInfo() 獲取到的用戶信息是一個灰色的頭像和微信用戶
				// 需要使用 uni.getUserProfile() 獲取用戶信息,此方法需要按鈕觸發(fā) @click=func
				return new Promise((resolve, reject) => {
					uni.getUserProfile({
						desc: '完善用戶信息',
						success: data => {
							console.log("用戶信息:", data)
							resolve(data)
						},
						fail: err => {
							reject(err)
						}
					})
				})
			},
			// 獲取 臨時登錄憑證code
			getAppCode() {
				return new Promise((resolve, reject) => {
					// #ifdef MP-WEIXIN
					uni.login({
						provider: 'weixin',
						success(res) {
							resolve(res.code)
						},
						fail(err) {
							reject(err)
						}
					})
					// #endif
					// #ifdef MP-ALIPAY
					// 系統(tǒng)建議使用支付寶原生寫法
					my.getAuthCode({
					  scopes: 'auth_base',
					  success(res) {
					  	resolve(res.authCode)
					  },
					  fail(err) {
					  	reject(err)
					  }
					 })
					// #endif
				})
			},
			// 用戶登錄
			appLogin(detail) {
				return new Promise(async(resolve, reject) => {
					try {
						// 微信登錄
						// #ifdef MP-WEIXIN
						let params = {
							code: this.code,
							source: 'MP',
							encryptedData: detail.encryptedData,
							iv: detail.iv
						}
						let wxloginRes = await loginByWx(params)
						if(wxloginRes.code == 200) {
							resolve(wxloginRes.data)
						} else {
							reject(wxloginRes)
						}
						// #endif
						// #ifdef MP-ALIPAY
						// 系統(tǒng)建議使用支付寶原生寫法
						let alipayloginRes = await loginByAlipay({
							code: this.code
						});
						if(alipayloginRes.code == 200) {
							resolve(alipayloginRes.data)
						} else {
							reject(alipayloginRes)
						}
						// #endif
					} catch(err) {
						reject(err)
					}
				});
			},
		}
	}
</script>

<style lang="less">
	view, text, image, input {
		box-sizing: border-box;
	}
	
	.content {
		position: relative;
		display: flex;
		flex-direction: column;
		align-items: center;
		width: 100%;
		height: 100vh;
		padding: 160rpx 40rpx 0;
		
		image {
			width: 275rpx;
			height: 104rpx;
		}
		.title {
			width: 100%;
			margin-top: 80rpx;
			font-size: 30rpx;
			font-weight: 600;
			color: rgba(0, 0, 0, 0.85);
			text-align: left;
		}
		.msg {
			display: block;
			width: 100%;
			margin-top: 16rpx;
			font-size: 28rpx;
			color: rgba(0, 0, 0, 0.65);
			text-align: left;
		}
		
		.btn {
			position: absolute;
			bottom: 160rpx;
			width: 670rpx;
			height: 96rpx;
			background: #00B391;
			border-radius: 8rpx;
			font-size: 34rpx;
			font-weight: 400;
			color: #FFFFFF;
			line-height: 96rpx;
		}
	}
</style>

原文鏈接:https://blog.csdn.net/weixin_45559449/article/details/129398318

欄目分類
最近更新