ChasenKaos原創(chuàng)文章,轉(zhuǎn)載請注明出處。
根據(jù)官方api的顯示,小程序的登錄流程如下:

1、用戶打開小程序,后臺(tái)自動(dòng)獲取code
2、開發(fā)者把"code"發(fā)送到服務(wù)器。
3、服務(wù)器把"code"、appid、secret等信息發(fā)送到微信官方接口。
4、官方返回一個(gè)session_key和用戶唯一的身份表示openid。
5、開發(fā)者通過官方返回的數(shù)據(jù)生成私有認(rèn)證token。
6、將token返回至客戶端。
7、用戶每次請求攜帶token。
完整代碼如下(node):
1、小程序app.js代碼
//app.js
//app.js
App({
onLaunch: function() {
// 展示本地存儲(chǔ)能力
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
// 獲取用戶信息
wx.getSetting({
success: res => {
if (res.authSetting['scope.userInfo']) {
// 已經(jīng)授權(quán),可以直接調(diào)用 getUserInfo 獲取頭像昵稱,不會(huì)彈框
wx.getUserInfo({
success: res => {
// 可以將 res 發(fā)送給后臺(tái)解碼出 unionId
this.globalData.userInfo = res.userInfo
// 由于 getUserInfo 是網(wǎng)絡(luò)請求,可能會(huì)在 Page.onLoad 之后才返回
// 所以此處加入 callback 以防止這種情況
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
}
})
}
}
})
},
globalData: {
userInfo: null
},
globalApi: {
checkUser: 'http://localhost:4000/checkUser/'
},
getToken() {
return new Promise((resolve, reject) => {
// 登錄
wx.login({
success: res => {
// 發(fā)送 res.code 到后臺(tái)換取 openId, sessionKey, unionId
if (res.code) {
//發(fā)送res.code 到后臺(tái)
wx.request({
url: this.globalApi.checkUser,
method: 'POST',
data: {
code: res.code
},
success(res) {
//成功返回?cái)?shù)據(jù)后,將token值存儲(chǔ)到localStorage中
wx.setStorage({
key: 'yerlLocalToken',
data: res.data.token
});
var resArg = res.data.token;
resolve()
},
fail() {
reject();
}
})
}
}
})
})
}
})
我是在本地環(huán)境測試的,所以api用的是本地地址,我把它放到了globalApi中方便調(diào)用。
2、小程序的頁面中調(diào)用token
///index.js
//獲取應(yīng)用實(shí)例
const app = getApp()
Page({
data: {
token:''
},
onLoad() {
let that = this;
app.getToken().then(function(){
that.setData({
token: wx.getStorageSync('yerlLocalToken')
});
console.log(that.data.token)
});
}
})
3、后臺(tái)(node + express + mongodb)
//express
const express = require('express');
const router = express.Router();
//處理formdata
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
const urlencodedParser = bodyParser.urlencoded({
extended: false
});
//處理node request請求
const request = require('request');
//token
const jwt = require('./jwt.js'); //這個(gè)是jsonwebtoken中的方法,我又進(jìn)行了二次加工。
//微信小程序設(shè)置
const wx = require('./wxconfig.json'); //文件中存儲(chǔ)了appid 和 secret
//數(shù)據(jù)庫
const datebase = require('./dbconfig.json') //文件中存儲(chǔ)了數(shù)據(jù)庫地址
const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient;
//路由
router.post('/', urlencodedParser, (req, res) => {
//拿到前臺(tái)給的code后,發(fā)送請求
if(req.body.code) {
let options = {
method: 'POST',
url: 'https://api.weixin.qq.com/sns/jscode2session?',
formData: {
appid: wx.appid,
secret: wx.secret,
js_code: req.body.code,
grant_type: 'authorization_code'
}
};
request(options, (error, response, body) => {
if(error) { //請求異常時(shí),返回錯(cuò)誤信息
res.json({
"status": "error",
"code": "ChasenKaso原創(chuàng)文章,轉(zhuǎn)載請注明出處"
})
} else {
//返回值的字符串轉(zhuǎn)JSON
let _data = JSON.parse(body);
//根據(jù)返回值創(chuàng)建token
let _l = jwt.createHoursToken(req.body.code, _data.openid, _data.session_key);
let _s = jwt.createMonthToken(req.body.code, _data.openid, _data.session_key);
//連接數(shù)據(jù)庫
MongoClient.connect(datebase.url, (err, client) => {
if(err) {
res.json({
"status": "error",
"code": "0002"
});
client.close();
} else {
const users = client.db(datebase.db).collection('Users'); //查詢數(shù)據(jù)庫中是否有當(dāng)前的openid
users.count({
"openid": _data.openid
}, (err, result) => {
//當(dāng)數(shù)據(jù)庫中沒有該openid時(shí),插入。
if(result == 0) {
users.insert({
"openid": _data.openid,
"localToken": _l,
"serverToken": _s
}, (err, result) => {
res.json({
"status": "ok",
"token": _l
});
client.close();
})
};
//當(dāng)數(shù)據(jù)庫中查詢到openid時(shí),更新token
if(result != 0) {
users.update({
"openid": _data.openid
}, {
$set: {
"localToken": _l,
"serverToken": _s
}
}, (err, result) => {
if(err) {
res.json({
"status": "error",
"code": "0003"
});
client.close();
} else {
res.json({
"status": "ok",
"token": _l
});
client.close();
}
})
}
})
}
})
}
});
} else {
res.json({
"status": "error",
"code": "0004"
});
}
})
module.exports = router;
在主程序中調(diào)用這個(gè)router就可以了,代碼如下:
const checkUser = require('./router/checkUser.js');
app.use('/checkUser',checkUser)
運(yùn)行后,顯示如下:

調(diào)試臺(tái)已經(jīng)顯示了后臺(tái)返回的token。
我把a(bǔ)pp.js中的用戶登錄使用promise進(jìn)行了封裝,這樣可以讓我在運(yùn)行小程序并打開index頁面時(shí),可以正常顯示token,否則會(huì)出現(xiàn)異步登錄未成功,頁面就已經(jīng)渲染完成,無法獲取到token的現(xiàn)象。