方式一:小程序授权登录

通过wx.login获取 临时登录凭证code,向后端换取token。 可以做到无感登录。

时序图:

说明:

1、客户端调用 wx.login() 获取 临时登录凭证code,通过 wx.request() 发起网络请求,将 code 传给服务端
2、服务端使用 code + appid + appsecret 向微信换取 (调用 auth.code2Session 接口)用户唯一标识openid 和 会话密钥session_key
3、服务端自定义 登录状态token(与openid、session_key关联)返回客户端
4、客户端将 登录状态token 存入 缓存storage(推荐使用 wx.setStorageSync(‘key’, ‘value’) 同步存储)
5、客户端wx.request() 发起请求时,携带 登录状态token (推荐使用 wx.getStorageSync(‘key’) 同步获取)
6、服务端通过 登录状态token 查询到对应 openid 和 session_key
7、验证成功后,返回业务数据给客户端

注意:

1、会话密钥session_key 是对⽤户数据进⾏加密签名的密钥。为了应⽤⾃身的数据安全,开发者服务器不应该把会话密钥下发到⼩程序,也不应该对外提供这个密钥。
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-WEIXINopenIdUrl = '微信登录接口地址'//#endif//#ifdef MP-ALIPAYopenIdUrl = '支付宝登录接口地址'//#endiflet 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-WEIXINuni.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
}

新建api.js,对接口进行封装

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

方式二:手机号授权登录

过button按钮的bindgetphonenumber事件,弹出手机号授权,获取到加密数据后,向后端换取token。

说明:

1、过button按钮的bindgetphonenumber事件获取手机号加密数据,按钮需要设置open-type=“getPhoneNumber”
2、调用 wx.login() 获取 临时登录凭证code
3、将加密数据(encryptedData、iv、signature、rawData)和 临时登录凭证code传给服务端
4、服务端使用 code + appid + appsecret 向微信换取 (调用 auth.code2Session 接口)用户唯一标识openid 和 会话密钥session_key
5、服务端根据session_key,appid ,encryptedData,iv解密手机号
6、服务端自定义 登录状态token(与openid、session_key关联)返回客户端
7、客户端将 登录状态token 存入 缓存storage(推荐使用 wx.setStorageSync(‘key’, ‘value’) 同步存储)
8、客户端wx.request() 发起请求时,携带 登录状态token (推荐使用 wx.getStorageSync(‘key’) 同步获取)
9、服务端通过 登录状态token 查询到对应 openid 和 session_key
10、验证成功后,返回业务数据给客户端

注意:

在回调中调⽤ wx.login 登录,可能会刷新登录态。此时服务器使⽤ code 换取的sessionKey 不是加密时使⽤的 sessionKey,导致解密失败。建议开发者提前进⾏ login;或者在回调中先使⽤ checkSession 进⾏登录态检查,避免 login刷新登录态。

也就是说在触发getPhoneNumber方法(用户点击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']),// 微信用户手机号登录getUserPhoneNumber(event) {if(event.detail.errMsg !== 'getPhoneNumber:ok') returnuni.showToast({title: '登录中',icon: 'loading',mask: true})event.detail.code = this.codethis.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})}})},// 获取codegetAppCode() {return new Promise((resolve, reject) => {// #ifdef MP-WEIXINuni.login({provider: 'weixin',success(res) {resolve(res.code)},fail(err) {reject(err)}})// #endif// #ifdef MP-ALIPAYmy.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 }) {// 微信手机号登录userInfo.type = 'WX_MP'delete userInfo.errMsgreturn 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')// 跳转登录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: () => {},})})
}

方式三:用户信息授权登录

通过button按钮的click事件,调用 wx.getUserProfile() 弹出授权框,获取到用户加密数据后,向后端换取token。

说明:

1、通过 wx.getUserProfile() 获取用户信息,此方法需要通过button按钮的click事件触发
2、调用 wx.login() 获取 临时登录凭证code
3、将加密数据(encryptedData、iv)和 临时登录凭证code传给服务端
4、服务端使用 code + appid + appsecret 向微信换取 (调用 auth.code2Session 接口)用户唯一标识openid 和 会话密钥session_key
5、服务端自定义 登录状态token(与openid、session_key关联)返回客户端,同时返回用户信息
6、客户端将 登录状态token 存入 缓存storage(推荐使用 wx.setStorageSync(‘key’, ‘value’) 同步存储)
7、客户端wx.request() 发起请求时,携带 登录状态token (推荐使用 wx.getStorageSync(‘key’) 同步获取)
8、服务端通过 登录状态token 查询到对应 openid 和 session_key
9、验证成功后,返回业务数据给客户端

注意:

在回调中调⽤ wx.login 登录,可能会刷新登录态。此时服务器使⽤ code 换取的sessionKey 不是加密时使⽤的 sessionKey,导致解密失败。建议开发者提前进⾏ login;或者在回调中先使⽤ checkSession 进⾏登录态检查,避免 login刷新登录态。

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

<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-WEIXINlet userData = await this._getwxUserData()// 调用后台接口登录let loginRes = await this.appLogin(userData)// savecacheuni.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-ALIPAYlet userData = await this._getalipayUserData()// 调用后台接口登录let loginRes = await this.appLogin(userData)loginRes.userName = userData.nickNameloginRes.headImg = userData.avatar// savecacheuni.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).responseresolve(userInfo)},fail: err => {reject(err)}})})},// 获取微信用户加密数据_getwxUserData() {// 用户信息接口调整,使用uni.getUserInfo() 获取到的用户信息是一个灰色的头像和微信用户// 需要使用 uni.getUserProfile() 获取用户信息,此方法需要按钮触发 @click=funcreturn new Promise((resolve, reject) => {uni.getUserProfile({desc: '完善用户信息',success: data => {console.log("用户信息:", data)resolve(data)},fail: err => {reject(err)}})})},// 获取 临时登录凭证codegetAppCode() {return new Promise((resolve, reject) => {// #ifdef MP-WEIXINuni.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-WEIXINlet 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>

微信小程序授权登录三种实现方式相关推荐

  1. 微信小程序页面之间三种传值方式

    第一种:url传值 A页面部分js代码 Page({jumpPage(e) {var zhi = 'biubiubiu~~';wx.navigateTo({url: '/pages/detail/de ...

  2. SpringCloud 微信小程序授权登录 获取openId SessionKey【SpringCloud系列13】

    SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见. 自我提升方法推荐:神奇的早起 早上 5:00 -5:20 起床刷牙 5:30-6:00 晨练(跑步.跳绳.骑自行车.打球等等) ...

  3. SpringBoot微信小程序授权登录

    SpringBoot微信小程序授权登录 一.appId 1.1.自己是管理者:微信公众平台,申请或登录自己的微信小程序,在开发者管理中即可看到 2.2.自己是开发者:让管理员将自己加入到小程序开发者管 ...

  4. 微信小程序开发的三种模式

    摘要:截止到2018年6月底,正式上线发布的微信小程序已超过100万个.而越来越多的公司也已经在做微信小程序开发,许多人会觉得"微信小程序开发是开发者们的专利".答案是否定的,今天 ...

  5. 基于Uniapp+SpringBoot实现微信小程序授权登录

    手把手教你做微信小程序授权登录交互 一.uni.login请求临时code 二.uni.request向后台交换数据 三.源代码 前台:在GetUserInfo中添加接口 后台:SpringBoot后 ...

  6. 微信小程序授权登录+获取手机号

    微信小程序授权登录+获取手机号 一.官方文档背景: 小程序登录的链接: 微信小程序获取手机号的链接: 二.微信小程序授权登录+获取手机号 1.简单说明官方文档的操作:先授权登录后拿手机号 2.前端代码 ...

  7. 微信小程序授权登录 组件的封装

    微信小程序授权登录 组件的封装 新建components文件 写wxml文件 wxss部分 js部分 json引用 页面使用 页面js 授权登录 流程如下: 因为多个页面功能需要登录状态 所以做了个组 ...

  8. uniapp微信小程序授权登录和获取微信绑定的手机号码

    uniapp微信小程序授权登录和获取微信绑定的手机号码 弹出授权的弹框 <view class="weixinOk" @tap="getUserProfile&qu ...

  9. 微信小程序授权登录取消授权重新授权处理方法 附可用代码

    微信小程序授权登录基本是小程序的标配了,但是官方的demo,取消授权后,就不能再重新点击登录,除非重新加载小程序才可以,这下怎么办? 我们可以先在首页引导用户点击,然后跳转到一个新的页面,在新的页面进 ...

最新文章

  1. GPU运行Tensorflow详细教程及错误解决
  2. mysql安装必要组件_openstack必须安装哪些组件
  3. 搜狗手机助手联合腾讯御安全 共建APP安全生态环境
  4. 若依管理系统源码分析-导出Excel时怎样通过注解中readConverterExp实现格式化导出列的显示格式(0和1格式化为是否)
  5. ABAP 引用类型介绍
  6. 循环链表解决约瑟夫环问题
  7. 转:SQL:外连接on条件与where条件的区别
  8. STP RSTP MSTP PVST+学习 (1)
  9. java 正则替换cdata_XML中配置正则表达式的写法
  10. 迅通雾霾处理技术为清晰都市保驾护航
  11. bt种子增加服务器,bt发布-BT种子怎么发布?服务器地址、发布者网址应怎么填写? 爱问知识人...
  12. java实现计算器_初学JAVA之实现计算器
  13. excel二极管伏安特性曲线_基础元件介绍—半导体二极管
  14. 学习java 的第一节课
  15. springboot+vue实现excel导入-------去重
  16. html a5 尺寸,纸型尺寸大小(A1,A2,A3,A4,A5,A6,B1,B2,B3,B4,B5......)
  17. java微信头像失效问题,将微信头像上传至七牛云
  18. 学习 Golang 开发,需要掌握这几个开源框架
  19. Python OpenSSL 解析证书
  20. linux 互斥机制

热门文章

  1. 高科技、互联网技术加持 智能鞋引领无限未来
  2. Discuz模板引擎标签
  3. SkeyeVSS智慧办公区视频监控解决方案
  4. 公司取名注意事项(如何取到合适的公司名)
  5. 北大博士的CVPR冠军体验:一群直男强行“认草”
  6. 中国石油和化工行业人工智能联盟正式成立;饿了么入局无人值守零售,取名“e点便利”...
  7. 自己制作电脑清理垃圾软件
  8. 分享一个自己利用Python写的淘宝秒杀工具源码
  9. 苹果手机删除的照片怎么恢复?3种有效方法推荐
  10. TortoiseGit同步项目到gitee步骤