客户端
开发版和测试版首次登录需要用户手动触发登录。比如点击指定登录组件
<button open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">新版登录</button>
对应的事件处理
Page({
bindGetUserInfo: function (e) {
console.log(e.detail.userInfo)
if (e.detail.userInfo) {
// 用户按了接受按钮
app.doLogin(this.getUserInfo);
} else {
//用户按了拒绝按钮
}
},
getUserInfo: function () {
let that = this;
let userInfo = app.globalData.userInfo;
console.info('userInfo is:', userInfo);
if (userInfo) {
that.setData({
hasLogin: true,
userInfo: userInfo
});
wx.hideLoading();
} else {
console.log('globalData中userInfo为空');
}
},
// 登录动作
doLogin: function (callback = () => { }) {
let that = this;
wx.login({
success: function (loginRes) {
log('wx.login执行成功', loginRes);
if (loginRes.code) {
/*
* @desc: 获取用户信息 期望数据如下
*
* @param: userInfo [Object]
* @param: rawData [String]
* @param: signature [String]
* @param: encryptedData [String]
* @param: iv [String]
**/
wx.getUserInfo({
withCredentials: true, // 非必填, 默认为true
success: function (infoRes) {
console.log('wx.getUserInfo成功', infoRes)
// 请求服务端的登录接口
wx.request({
url: api.loginUrl,
data: {
code: loginRes.code, // 临时登录凭证
rawData: infoRes.rawData, // 用户非敏感信息
signature: infoRes.signature, // 签名
encryptedData: infoRes.encryptedData, // 用户敏感信息
iv: infoRes.iv // 解密算法的向量
},
success: function (res) {
console.log('login success');
res = res.data;
if (res.result == 0) {
that.globalData.userInfo = res.userInfo;
wx.setStorageSync('userInfo', JSON.stringify(res.userInfo));
wx.setStorageSync('loginFlag', res.skey);
callback();
} else {
that.showInfo(res.errmsg);
}
},
fail: function (error) {
// 调用服务端登录接口失败
log('调用服务端登录接口失败', error);
that.showInfo('调用接口失败');
console.log(error);
}
});
},
fail: function (error) {
// 获取 userInfo 失败,去检查是否未开启权限
log('获取 userInfo 失败');
wx.hideLoading();
that.checkUserInfoPermission();
}
});
} else {
// 获取 code 失败
that.showInfo('登录失败');
console.log('调用wx.login获取code失败');
}
},
fail: function (error) {
// 调用 wx.login 接口失败
that.showInfo('接口调用失败');
console.log(error);
}
});
},
})
服务端
根据客户端传过来的临时登录凭证
,以及小程序注册时获取的appid
和appSecret
,可以从微信服务器中拿到登录态标识session_key,进而拿到这个用户的唯一标识openId
和其它非敏感信息。
这个唯一标识openId
也可以作为你的业务系统中用户的唯一标识。
const http = require('axios');
const crypto = require('crypto');
const { appConfig: config } = require('../conf/app');
const { decryptByAES, encryptSha1 } = require('../util/util');
const { saveUserInfo } = require('../controllers/users');
/**
* 登录校验中间件
*/
function authorizeMiddleware(req, res, next) {
return authMiddleware(req).then(function (result) {
// 将结果存入响应信息的'auth_data'字段
res['auth_data'] = result;
return next();
})
}
function authMiddleware(req) {
const {
appid,
secret
} = config;
const {
code,
encryptedData,
iv
} = req.query;
// 检查参数完整性
if ([code, encryptedData, iv].some(item => !item)) {
return {
result: -1,
errmsg: '缺少参数字段,请检查后重试'
}
}
// 获取 session_key和 openid
return getSessionKey(code, appid, secret)
.then(resData => {
// 选择加密算法生成自己的登录态标识
const { session_key } = resData;
const skey = encryptSha1(session_key);
let decryptedData = JSON.parse(decryptByAES(encryptedData, session_key, iv));
console.log('-------------decryptedData---------------');
console.log(decryptedData);
console.log('-------------decryptedData---------------');
// 存入用户数据表中
return saveUserInfo({
userInfo: decryptedData,
session_key,
skey
})
})
.catch(err => {
return {
result: -3,
errmsg: JSON.stringify(err)
}
})
}
/**
* 获取当前用户session_key
* @param {*用户临时登录凭证} code
* @param {*小程序appid} appid
* @param {*小程序密钥} appSecret
*/
function getSessionKey(code, appid, appSecret) {
const opt = {
method: 'GET',
url: 'https://api.weixin.qq.com/sns/jscode2session',
params: {
appid: appid,
secret: appSecret,
js_code: code,
grant_type: 'authorization_code'
}
};
return http(opt).then(function (response) {
const data = response.data;
if (!data.openid || !data.session_key || data.errcode) {
return {
result: -2,
errmsg: data.errmsg || '返回数据字段不完整'
}
} else {
console.log('jscode2session结果',data);
return data
}
});
}
module.exports = {
authorizeMiddleware
}