微信小程序搭配后端实现用户注册登录颁发token凭证
微信小程序登录逻辑
说明:
调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 和 会话密钥 session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
注意:
会话密钥 session_key 是对用户数据进行加密签名的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。
临时登录凭证 code 只能使用一次。
个人开发思路
微信小程序端,通过wx.login()可以获取到一个临时的登录凭证,然后将这个code发送到服务端,服务端将code与配置信息(appid、appsecret)一同发送到微信服务端换取用户的唯一凭证openid(相当于QQ号,每个用户独一无二)。如果这个时候用户不存在,那就创建,然后将openid存入数据库中再返回token给用户。要是用户存在就直接返回登录的token给用户。
微信小程序端
小程序配置 ./config/config.js
module.exports = {
api: 'http://localhost:3000/mini/api',
}
小程序最主要界面app.js相关配置
const config = require('./config/config.js');
App({
globalData: {
info:''
},
onLaunch: function () {
this.logIn();
},
logIn: function () {
return new Promise(function (resolve, reject) {
wx.login({
success: (res) => {
let code = res.code
if (code) {
wx.setStorageSync('user', res.userInfo)
wx.request({
url: `${config.api}/login`,
method: 'POST',
data: {
code: code
},
header: {
'content-type': 'application/json'
},
success: function (res) {
console.log(res.data.token)
wx.setStorageSync('token', res.data.token)
resolve(res)
}
})
} else {
console.log('获取用户登录态失败!' + res.errMsg)
reject(err)
}
}
});
})
},
checkToken: function () {
console.log('Promise');
const token = wx.getStorageSync('token')
return new Promise(function (resolve, reject) {
wx.request({
url: `${config.api}/auth`,
header: {
'authorization': token
},
method: 'POST',
success(res) {
console.log(res);
resolve(res)
},
fail(err) {
console.log(err);
reject(err)
}
})
})
}
})
服务端
创建数据库模型 Quser.js
const mongoose = require('mongoose')
const UserSchema = new mongoose.Schema(
{
openId: { type: String }, //唯一Openid标识
nickname: { type: String }, //用户昵称
unionid: { type: String },
headimgurl: { type: String }, //用户头像
gender: { type: String }, //用户性别,1男2女
},
{ timestamps: true }
)
module.exports = mongoose.model('Quser', UserSchema)
创建路由模块 mini/index.js 和 quser.js
首先我们看index.js
module.exports = app => {
const express = require('express')
const router = express.Router()
const qusers = require('./quser')
//检测token是否过期
router.post('/auth', qusers.checkToken)
//换取token
router.post('/login', qusers.login)
//初始化用户资料
router.post('/user', qusers.checkToken, async (req, res) => {
const model = await req.Model.create(req.body)
res.send(model)
})
app.use('/mini/api/', async (req, res, next) => {
next()
}, router)
}
我们接着看quser.js
const jwt = require('jsonwebtoken')
const User = require('../../models/Quser')
const Qdetile = require('../../models/Qdetile')
const System = require('../../models/System')
const config = require('../../plugins/config')
const axios = require('axios')
let generateToken = function (user) {
return jwt.sign(user, config.jwtSecret, {
expiresIn: 7200
})
}
//获取页面初始化资料
exports.now = async (req, res) => {
const queryOptions = {}
const model = await System.find().setOptions(queryOptions)
return res.json({
model
})
}
exports.login = (req, res) => {
const queryString = `appid=${config.appId}&secret=${config.appSecret}&js_code=${req.body.code}&grant_type=authorization_code`;
const wxAPI = `https://api.weixin.qq.com/sns/jscode2session?${queryString}`;
axios.get(wxAPI)
.then(response => {
User.findOne({ openId: response.data.openid }, (err, user) => {
if (user) {
console.log(user)
return res.json({
token: generateToken({ openid: response.data.openid })
})
} else {
const user = new User();
user.openId = response.data.openid;
user.save();
return res.json({
token: generateToken({
openid: response.data.openid
})
})
}
})
})
.catch(error => {
console.log(error)
})
}
exports.checkToken = (req, res, next) => {
let token = req.headers.authorization;
console.log(token);
if (token) {
console.log('token exist');
jwt.verify(token, config.jwtSecret, (err, decoded) => {
console.log('jwt.verify');
if (err) {
console.log('err');
if (err.name === 'TokenExpiredError') {
console.log('认证码失效,请重新登录!');
return res.status(401).json({ error: '认证码失效,请重新登录!' });
} else {
console.log('认证失败!');
return res.status(401).json({ error: '认证失败!' });
}
} else {
if (decoded.openid) {
req.openid = decoded.openid;
console.log('req.openid = decoded.openid;');
return res.status(200).json({ message: '已登录' });
} else {
console.log('认证失败!');
res.status(401).json({ error: '认证失败!' });
}
}
});
} else {
console.log("no token");
return res.status(403).json({
error: '请提供认证码!'
});
}
}
config.js 配置信息
module.exports = {
// db uri
db: 'mongodb://127.0.0.1:27017/test',
// Setting port for server
port: 3000,
host: 'http://localhost:3000',
jwtSecret: '',
appId: '',
appSecret: '',
}