uni-app微信授权,新版4.28,多种方式校验临时凭证code来获取openid
- 前言
- 一、授权流程
- 1.流程图
- 2.授权操作
- 1.加载登录页判断缓存,自动登录
- 2.授权同意与拒绝
- 3.获取服务供应商
- 4.微信登录
- 5.换取openid标识
- 3.完整代码
- 二、授权手机号
- 1.Button
- 2.解密数据
- 3.WXBizDataCrypt.js
- 总结
前言
注意:
- 2021年4月28日24时后发布的小程序新版本,无法通过wx.getUserInfo与
open-type
= getUserInfo 获取用户个人信息 - 新增
getUserProfile
接口,可获取用户头像、昵称、性别及地区信息,每次通过该接口获取用户个人信息均需用户确认
好家伙,3月份做的登录授权到4月就不能用了,那就只能乖乖听话改方法了。
不过新版方式可以组件调用授权方法,只要使用了uni.getUserProfile(OBJECT) 方法才会弹出授权窗口,也还是挺方便的
一、授权流程
1.流程图
附一张官方图
这是腾讯官方规定的,想要兑换唯一凭证,就必须要通过后端安全处理,假设你的uni-app中用 request
或者uniCloud云函数
方式获取到了openid,那么发布上线后依然是不能用的,小程序会对合法域名进行严格校验
简要说明:
- 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器
- 后端通过调用接口换取用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key
2.授权操作
首先定义个三方登录按钮吧
样式可自定义,这里就是一个普通按钮,上面注释掉的Button为旧的授权登录方式
<view class="footer">
<view class="other_login">
<!-- <button class="btn-phone" open-type="getUserInfo" @getuserinfo="appLoginWx"></button> -->
<button class="btn-phone" @click="appLoginWx"></button>
</view>
<text class="text-white">WeChat</text>
</view>
下面开始分步处理
1.加载登录页判断缓存,自动登录
onLoad() {
let u = uni.getStorageSync('userInfo')
if (u._id) {
uni.showLoading({
title:"登录中...",
mask: true
})
setTimeout(()=>{
uni.hideLoading()
uni.switchTab({
url: '/pages/index/index'
})
},1000)
}
},
2.授权同意与拒绝
let self = this
uni.getUserProfile({
desc: '向小红帽报告一下信息',
success: userProfile => {
console.log('微信用户数据', userProfile)
// 同意授权后
// ...
},
fail: () => {
uni.showToast({
title: "您取消了授权",
icon: "none",
duration: 2000
})
}
});
userInfo
: 用户信息对象rawData
: 不包括敏感信息的原始数据字符串,用于计算签名signature
: 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息encryptedData
: 包括敏感数据在内的完整用户信息的加密数据iv
: 加密算法的初始向量cloudID
: 敏感数据对应的云 ID,开通云开发的小程序才会返回,可通过云调用直接获取开放数据errMsg
: 描述信息
我们暂时用到userInfo里的数据:头像,昵称,性别
3.获取服务供应商
获取当前工程环境中可用的供应商
uni.getProvider({
service: 'oauth',
success: wxProvider => {
console.log('服务供应商', wxProvider)
if (~wxProvider.provider.indexOf('weixin')) {
// 可使用weixin供应商,开始微信登录
// ...
} else {
uni.showToast({
title: '请先安装微信或升级版本',
icon: "none",
duration: 2000
});
}
}
});
我们这里是授权登录,所以service服务类型是oauth,provider是得到的服务供应商
oauth
: 登录类share
: 分享类payment
: 支付类push
: 推送类
4.微信登录
uni.login({
provider: 'weixin',
success: wxCodeRes => {
console.log('微信CODE响应', wxCodeRes)
// 使用临时登录凭证CODE换取唯一凭证
// ...
},
fail: () => {
uni.showToast({
title: "微信登录失败",
icon: "none",
duration: 2000
});
}
})
然后才是重头戏,我们有很多很多方式可以获取唯一凭证,下面我举两个例子,一个是利用 uniCloud
云函数,一个是利用 Nodejs Express
服务器,其实最好还是交给java处理
5.换取openid标识
uniCloud 云函数 wx_login
'use strict';
const loginConfig = {
AppId: '', //微信小程序AppId
AppSecret: '' //微信小程序AppSecret
}
exports.main = async (event, context) => {
let data = {
appid: loginConfig.AppId,
secret: loginConfig.AppSecret,
js_code: event.code,
grant_type: 'authorization_code'
}
const res = await uniCloud.httpclient.request('https://api.weixin.qq.com/sns/jscode2session', {
method: 'GET',
data,
dataType: 'json'
})
const success = res.status === 200 && res.data && res.data.openid
if (!success) {
return {
status: 500,
msg: '从微信获取登录信息失败'
}
} else {
return {
data: res.data,
msg: '获取OPENID&SESSIONKEY成功'
}
}
};
简单说明:
- 请求参数
- url:https://api.weixin.qq.com/sns/jscode2session
- method:GET
- 参数
- appid:你的小程序APPID
- secret:你的小程序APPSECRET
- js_code:临时凭证
- grant_type:authorization_code
调用代码
uniCloud.callFunction({
name: 'wx_login',
data: { code: wxCodeRes.code },
success(wxOpenRes) {
console.log('微信OPEN响应', wxOpenRes)
// 对接数据库操作账号
// ...
},
fail(e) {
self.loadWxModal(false)
console.log(e)
}
})
express服务器换取
import { fetchGet , fetchPost } from './utils/selfhttp.js'
export default (query, request) => {
return new Promise((resolve, reject) => {
fetchGet('https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code='+
query.code+'&grant_type=authorization_code ', {})
.then(res => {
resolve(res.data)
})
.catch((e) => {
reject(e)
})
})
}
为了测试,简单使用了下axios
import axios from 'axios'
// 响应时间
axios.defaults.timeout = 8 * 1000
// 配置cookie
// axios.defaults.withCredentials = true
// 配置请求头
// axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'
// 配置接口地址
axios.defaults.baseURL = ''
// POST传参序列化(添加请求拦截器)
axios.interceptors.request.use(
config => {
return config
},
err => {
return Promise.reject(err)
}
)
// 返回状态判断(添加响应拦截器)
axios.interceptors.response.use(
res => {
return res
},
err => {
return Promise.reject(err)
}
)
// 发送请求
export function fetchPost (url, params={}) {
return new Promise((resolve, reject) => {
axios
.post(url, params)
.then(
res => {
resolve(res)
},
err => {
reject(err)
}
)
.catch(err => {
reject(err)
console.log(err);
})
})
}
export function fetchGet (url, params={}) {
return new Promise((resolve, reject) => {
axios
.get(url, {
params: params
})
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
}
调用代码
切记,你的后端需要时https协议并在小程序管理平台添加为合法域名,否则上线后将功亏一篑,你们可以在各自后端中发送相应的请求和参数,总之最后获取到 openid
和 session_key
就行了
self.$http.get('后端URL', { code: wxCodeRes.code }, {tk: '自定义后端访问TOKEN'}, {})
.then(wxOpenRes => {
console.log('微信OPEN响应', wxOpenRes)
self.userInfo.openid = wxOpenRes.openid
self.userInfo.session_key = wxOpenRes.session_key
self.userInfo.nickName = u.nickName
self.userInfo.avatarUrl = u.avatarUrl
self.userInfo.gender = u.gender
// 查询数据库是否有该账号
// ...
// 有则直接登录
// 无则添加后再登录
})
3.完整代码
// 控制加载弹窗
loadWxModal(e) {
this.loadModal = e
},
appLoginWx() {
let self = this
uni.getUserProfile({
desc: '向小红帽报告一下信息',
success: userProfile => {
console.log('微信用户数据', userProfile)
self.loadWxModal(true)
let u = userProfile.userInfo
uni.getProvider({
service: 'oauth',
success: wxProvider => {
console.log('服务供应商', wxProvider)
if (~wxProvider.provider.indexOf('weixin')) {
uni.login({
provider: 'weixin',
success: wxCodeRes => {
console.log('微信CODE响应', wxCodeRes)
self.$http.get('https://xxx/wx/login', { code: wxCodeRes.code }, {tk: 'xxx'}, {})
.then(wxOpenRes => {
console.log('微信OPEN响应', wxOpenRes)
self.userInfo.openid = wxOpenRes.openid
self.userInfo.session_key = wxOpenRes.session_key
self.userInfo.nickName = u.nickName
self.userInfo.avatarUrl = u.avatarUrl
self.userInfo.gender = u.gender
self.userInfo.createTime = new Date()
self.userInfo.userPhone = ''
// 查询数据库是否有该账号,有则登录,无则添加
// ...
})
},
fail: () => {
self.loadWxModal(false)
uni.showToast({
title: "微信登录失败",
icon: "none",
duration: 2000
});
}
})
} else {
self.loadWxModal(false)
uni.showToast({
title: '请先安装微信或升级版本',
icon: "none",
duration: 2000
});
}
}
});
},
fail: () => {
uni.showToast({
title: "您取消了授权",
icon: "none",
duration: 2000
})
}
});
}
当然也可分开同步封装调用,也要记得用户登录存缓存一份
二、授权手机号
这个就简单也说下吧,但是慎重,乱用会被腾讯制裁的,毕竟直接把人家手机号都怼到手了,这个被举报了很严重的,咳咳
不过非企业是无法在小程序中进行微信认证,也就是说只能测试玩玩,上线后不能使用该功能
1.Button
<view class="margin-top padding-lr-xl flex flex-direction">
<button class="btn-wx" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">获取手机号</button>
</view>
2.解密数据
导入工具类
import WXBizDataCrypt from '@/common/WXBizDataCrypt.js'
授权获取手机号流程
- 需要你的appid
- 需要你当前登录的账号的session_key
- 需要你同意授权,然后获取到2个值
- 对以上4条数据进行解密就能得到你想要的了
// 获取wechat手机号
getPhoneNumber(e) {
let self = this
if (e.detail.errMsg === "getPhoneNumber:ok") {
let c = new WXBizDataCrypt(self.appid, self.userInfo.session_key);
let data = c.decryptData(e.detail.encryptedData, e.detail.iv);
self.userInfo.userPhone = data .phoneNumber
// 修改微信账号手机号
// ...
} else {
uni.showToast({
title: "已拒绝获取微信手机号",
icon: "none",
duration: 2000
})
}
},
如果授权同意将获取到 encryptedData
和 iv
,然后解密后的数据就是我们需要的了
phoneNumber
: 用户绑定的手机号(国外手机号会有区号)purePhoneNumber
: 没有区号的手机号countryCode
: 区号
3.WXBizDataCrypt.js
var crypto = require('crypto')
function WXBizDataCrypt(appId, sessionKey) {
this.appId = appId
this.sessionKey = sessionKey
}
WXBizDataCrypt.prototype.decryptData = function (encryptedData, iv) {
// base64 decode
var sessionKey = new Buffer(this.sessionKey, 'base64')
encryptedData = new Buffer(encryptedData, 'base64')
iv = new Buffer(iv, 'base64')
try {
// 解密
var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv)
// 设置自动 padding 为 true,删除填充补位
decipher.setAutoPadding(true)
var decoded = decipher.update(encryptedData, 'binary', 'utf8')
decoded += decipher.final('utf8')
decoded = JSON.parse(decoded)
} catch (err) {
throw new Error('Illegal Buffer')
}
if (decoded.watermark.appid !== this.appId) {
throw new Error('Illegal Buffer')
}
return decoded
}
module.exports = WXBizDataCrypt
总结
小程序上线坑很多,大家需要好好填充