Três métodos de implementação do login de autorização do miniaplicativo WeChat

Método 1: Login autorizado do miniprograma

Obtenha o código de credencial de login temporário por meio de wx.login e troque o token com o back-end. Você pode fazer login sem sentir.

Diagrama de tempo:

insira a descrição da imagem aqui

ilustrar:

1. O cliente chama wx.login() para obter o código de credencial de login temporário, inicia uma solicitação de rede por meio de wx.request() e envia o código para o servidor. 2. O servidor
usa code + appid + appsecret para trocar por WeChat (call auth.code2Session Interface) O identificador exclusivo do usuário openid e a chave de sessão session_key
3. O servidor personaliza o token de status de login (associado a openid e session_key) e retorna ao cliente
4. O cliente armazena o token de status de login no cache armazenamento (recomendado para usar wx.setStorageSync('chave', 'valor') armazenamento síncrono)
5. Quando o cliente wx.request() inicia uma solicitação, ele carrega o token de status de login (recomendado para usar wx.getStorageSync('chave ') para obtê-lo de forma síncrona)
6. O servidor consulta através do token de status de login Para o openid e session_key correspondentes
7. Após a verificação ser bem-sucedida, retorne os dados de negócios para o cliente

Perceber:

1. A chave de sessão session_key é a chave para criptografar e assinar os dados do usuário. Para proteger os dados do próprio aplicativo, o servidor de desenvolvimento não deve entregar a chave de sessão ao applet, nem deve fornecer essa chave para o mundo externo.
2. O código de credencial de login temporário só pode ser usado uma vez

código (pegue o framework uni-app como exemplo):

Crie um novo http.js para encapsular o método de login

// 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)

    // 再次发起请求
    return await _request(url, method, data)

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

// 发送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
    // 系统建议使用支付宝原生写法
    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
}

Crie um novo api.js para encapsular a interface

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

Método 2: login autorizado por número de celular

Através do evento bindgetphonenumber do botão button, aparece a autorização do número do celular, e após obter os dados criptografados, troca o token com o backend.

ilustrar:

1. Obtenha os dados criptografados do número do celular por meio do evento bindgetphonenumber do botão do botão. O botão precisa ser definido como open-type="getPhoneNumber"
2. Chame wx.login() para obter o código de credencial de login temporário
3 . Os dados criptografados (encryptedData, iv, signature, rawData ) e o código de credencial de login temporário para o servidor
4. O servidor usa code + appid + appsecret para trocar por WeChat (chamada auth.code2Session interface) o identificador exclusivo openid e sessão do usuário key session_key
5. O servidor usa session_key, appid,cryptedData, iv Descriptografa o número do celular
6. O servidor personaliza o token de status de login (associado a openid e session_key) e o retorna ao cliente
7. O cliente armazena o token de status de login no armazenamento em cache (recomendado usar wx.setStorageSync('chave', 'valor') Armazenamento síncrono)
8. Quando o cliente wx.request() inicia uma solicitação, ele carrega o token de status de login (recomendado usar wx.getStorageSync ('key') para obtê-lo de forma síncrona)
9. O servidor consulta o openid e a session_key correspondentes por meio do token de status de login
10. Após a verificação ser bem-sucedida, retorne os dados de negócios ao cliente

Perceber:

Chame wx.login no retorno de chamada para fazer login e o status de login pode ser atualizado. Neste momento, o sessionKey trocado pelo servidor pelo código não é o sessionKey usado para criptografia, resultando em uma falha de descriptografia. Recomenda-se que os desenvolvedores façam login com antecedência; ou use checkSession para verificar o status de login no retorno de chamada para evitar a atualização do status de login após o login.

Ou seja, antes que o método getPhoneNumber seja acionado (o usuário clica no botão), o código mais recente precisa ser obtido.

código (pegue o framework uni-app como exemplo):

novo 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']),
      // 微信用户手机号登录
      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) {
    
     // 登录成功,跳转首页
            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>

Crie um novo store/index.js para encapsular a lógica de login

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 }) {
    
    
      // 微信手机号登录
      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

Introduzir vuex em main.js e montá-lo na instância vue

//引入vuex 并且挂载到vue实例上
import store from "./store/index.js"
Vue.prototype.$store = store

pacote 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')
          // 跳转登录
          uni.redirectTo({
    
     url: '/pages/login/index' })
          reject(res.data)
        } else {
    
     // 返回内容
          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')
          // 跳转登录
          uni.redirectTo({
    
     url: '/pages/login/index' })
          reject(res.data)
        } else {
    
     // 返回内容
          resolve(JSON.parse(res.data))
        }
      },
      error: err => {
    
    
        console.error("请求失败", err)
        reject(err)
      },
      complete: () => {
    
    },
    })
  })
}

Método 3: login de autorização de informações do usuário

Através do evento click do botão, chame wx.getUserProfile() para abrir a caixa de autorização Após obter os dados criptografados do usuário, troque o token com o backend.

ilustrar:

1. Obtenha informações do usuário através de wx.getUserProfile(). Este método precisa ser acionado pelo evento click do botão.
2. Chame wx.login() para obter o código de credencial de login temporário
3. Combine dados criptografados (encryptedData, iv) e código de credencial de login temporário Passe-o para o servidor
4. O servidor usa código + appid + appsecret para trocar por WeChat (chamada interface auth.code2Session) o identificador exclusivo do usuário openid e chave de sessão session_key
5. O servidor personaliza o status de login token (associado com openid e session_key) e retorna O cliente retorna as informações do usuário ao mesmo tempo
6. O cliente armazena o token de status de login no armazenamento em cache (recomendado usar wx.setStorageSync('key', 'value') para sincronização storage)
7. Quando o cliente wx.request() inicia uma solicitação, carrega o token de status de login (wx.getStorageSync('key') é recomendado para obter de forma síncrona)
8. O servidor consulta o openid e a session_key correspondentes por meio do status de login token
. 9. Após a verificação bem-sucedida, retorne os dados comerciais ao cliente

Perceber:

Chame wx.login no retorno de chamada para fazer login e o status de login pode ser atualizado. Neste momento, o sessionKey trocado pelo servidor pelo código não é o sessionKey usado para criptografia, resultando em uma falha de descriptografia. Recomenda-se que os desenvolvedores façam login com antecedência; ou use checkSession para verificar o status de login no retorno de chamada para evitar a atualização do status de login após o login.

código (pegue o framework uni-app como exemplo):

<template>
	<view class="content">
		<image src="logo.png"></image>
		<view class="title">申请获取以下权限</view>
		<text class="msg">获取你的公开信息(昵称、头像、地区等)</text>
		<!-- #ifdef MP-WEIXIN -->
		<button class="btn" @click="wxgetUserInfo">授权登录</button>
		<!-- #endif -->
		<!-- #ifdef MP-ALIPAY -->
		<button class="btn" open-type="getAuthorize" @getAuthorize="alipaygetUserInfo" @error="onAuthError" scope='userInfo'>授权登录</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()
					// 调用后台接口登录
					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()
					// 调用后台接口登录
					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()
				}
			},
			// 授权失败
			onAuthError() {
    
    
				uni.showToast({
    
    
					title: '授权失败,请确认授权已开启',
					mask: true,
					icon: 'none'
				})
			},
			// 获取支付宝用户加密数据
			_getalipayUserData() {
    
    
				return new Promise((resolve, reject) => {
    
    
					my.getOpenUserInfo({
    
    
						success: res => {
    
    
							let userInfo = JSON.parse(res.response).response
							resolve(userInfo)
						},
						fail: err => {
    
    
							reject(err)
						}
					})
				})
			},
			// 获取微信用户加密数据
			_getwxUserData() {
    
    
				// 用户信息接口调整,使用uni.getUserInfo() 获取到的用户信息是一个灰色的头像和微信用户
				// 需要使用 uni.getUserProfile() 获取用户信息,此方法需要按钮触发 @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
					// 系统建议使用支付宝原生写法
					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
						// 系统建议使用支付宝原生写法
						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>

Acho que você gosta

Origin blog.csdn.net/weixin_45559449/article/details/129398318
Recomendado
Clasificación