微信小程序登录授权流程
openid:用户的唯一标识
session_key:本次登录的会话密钥(保证当前用户进行会话操作的有效性)
1. 调用wx.login生成code
wx.login() 这个API的作用就是为当前用户生成一个临时的登录凭证,这个临时登录凭证的有效期只有五分钟。
wx.login({
success: res => {
if (res.code) {
app.globalData.code = res.code; // 获得code 存入全局
} else {
console.log('获取用户登录态失败!' + res.errMsg);
that.onLogin(); // 重新登录
}
},
});
2. 获取openid和session_key(后台服务器)
我们拿到code
这个登录凭证后就可以 在服务器后台调用 auth.code2Session接口,完成登录流程。(使用 code 换取 openid
和 session_key
等信息。)
【注意:必须要在本项目后台服务中才能成功,在小程序端是无效的】
这个接口需要带上四个请求参数
参数 | 值 | 必填 |
---|---|---|
appid | 小程序 appId | 是 |
secret | 小程序 appSecret | 是 |
js_code | 前面调用wx.login派发的code | 是 |
grant_type | 授权类型,此处只需填写 ‘authorization_code’ | 是 |
- session_key和微信派发的code是一一对应的,同一code只能换取一次session_key。每次调用wx.login(),都会下发一个新的code和对应的session_key,为了保证用户体验和登录态的有效性,开发者需要清楚用户需要重新登录时才去调用wx.login()
- session_key是有时效性的,即便是不调用wx.login,session_key也会过期,过期时间跟用户使用小程序的频率成正相关,但具体的时间长短开发者和用户都是获取不到的
function getSessionKey(code, appid, appSecret) {
wx.request({
url: 'https://api.weixin.qq.com/sns/jscode2session',
data: {
appid: appid,
secret: appSecret,
js_code: code,
grant_type: 'authorization_code',
},
method: 'GET',
success: function (res) {
var obj = {
openid: res.data.openid, // 用户唯一标识
session_key: res.data.session_key, // 会话密钥
unionid: res.data.unionid, // 用户在开放平台的唯一标识符
};
wx.setStorageSync('user', obj);
},
});
}
3. 生成3rd_session
如果直接使用微信服务端派发的session_key来作为业务方的登录态使用,会被“有心之人”用来获取用户的敏感信息,比如wx.getUserInfo()这个接口呢,就需要session_key来配合解密微信用户的敏感信息。
那么我们如果生成自己的登录态标识呢,这里可以使用几种常见的不可逆的哈希算法,比如md5、sha1等,将生成后的登录态标识(这里我们统称为’skey’)返回给前端,并在前端维护这份登录态标识(一般是存入storage)。而在服务端呢,我们会把生成的skey存在用户对应的数据表中,前端通过传递skey来存取用户的信息。
可以看到这里我们使用了sha1算法来生成了一个skey:
const crypto = require('crypto');
function encryptSha1(data) {
return crypto.createHash('sha1').update(data, 'utf8').digest('hex');
}
const skey = encryptSha1(session_key);
wx.setStorageSync(skey, skey);
4. 调用wx.checkSession()校验当前session_key是否已经过期
wx.checkSession() 这个API并不需要传入任何有关session_key的信息参数,而是微信小程序自己去调自己的服务来查询用户最近一次生成的session_key是否过期。如果当前session_key过期,就让用户来重新登录,更新session_key,并将最新的skey存入用户数据表中。
let loginFlag = wx.getStorageSync('skey');
if (loginFlag) {
wx.checkSession({ // 检查 session_key 是否过期
success: function () { //session_key 未过期,并且在本生命周期一直有效
// 业务逻辑处理
},
// session_key 过期
fail: function () { // session_key过期,重新登录
that.onLogin();
},
});
} else {
// 无skey,作为首次登录
that.onLogin();
}
登录授权流程
function onLogin() {
const that = this;
wx.login({
success: res => {
if (res.code) {
app.globalData.code = res.code; // 获得code 存入全局
try {
canIUseFlag = wx.canIUse('getSetting');
} catch (canIuseEX) {}
if (canIUseFlag) {
wx.getSetting({
// 获取用户的当前设置 判断是否授权
success: res => {
// 表示scope.userInfo这个权限已经授权。
if (res.authSetting['scope.userInfo']) {
// 调用wx.getUserInfo 获取用户信息
that.getUserInfo();
} else {
// 表示scope.userInfo这个权限未授权。
// 向用户发起 scope.userInfo 授权请求
wx.authorize({
scope: 'scope.userInfo',
success(rs) {
// 调用wx.getUserInfo 获取用户信息
that.getUserInfo();
},
fail(rs) {
console.log(rs);
},
});
util.hideLoading();
return;
}
},
fail(rs) {
console.log('用户暂未授权');
},
});
}
} else {
that.onLogin();
}
},
fail(rs) {
that.onLogin();
},
});
util.hideLoading();
}
// 获取用户信息
function getUserInfo() {
// 必须是在用户已经授权的情况下调用
wx.getUserInfo({
success: function (res) {
let userInfo = res.userInfo;
let wx_user_info = {
nickName: userInfo.nickName,
avatarUrl: userInfo.avatarUrl,
gender: userInfo.gender, //性别 0:未知、1:男、2:女
province: userInfo.province,
city: userInfo.city,
country: userInfo.country,
};
wx.setStorageSync('wx_user_info', wx_user_info);
},
});
}