前言
創(chuàng)新互聯(lián)-專(zhuān)業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性?xún)r(jià)比港北網(wǎng)站開(kāi)發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式港北網(wǎng)站制作公司更省心,省錢(qián),快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋港北地區(qū)。費(fèi)用合理售后完善,十余年實(shí)體公司更值得信賴(lài)。
接著上遍文章(使用session保存用戶(hù)數(shù)據(jù))來(lái)讓使用jwt保存用戶(hù)數(shù)據(jù)。
這里會(huì)用到passport-jwt/jsonwebtoken。
passport-jwt是passport的一個(gè)驗(yàn)證策略。它使用jwt(json web token)驗(yàn)證。
jsonwebtoken是一個(gè)編碼、解碼、驗(yàn)證jwt的模塊。
使用jwt保存用戶(hù)數(shù)據(jù)與使用session保存用戶(hù)數(shù)據(jù)對(duì)比
session | json web token | |
---|---|---|
保存在server | 保存在client |
因session保存在server,所以服務(wù)器壓力比較大。聽(tīng)說(shuō)并發(fā)量達(dá)到1k時(shí)就能看到效果。
因jwt保存在client,所以需要加密。
使用jwt
1. 安裝依賴(lài)。
npm i passport-jwt jsonwebtoken
2. 創(chuàng)建一個(gè)配置文件,引用配置是使用。
// ./config.js module.exports = { secretKey: '12345-67890-9876-54321', mongoUrl: 'MongoDB://localhost:27017/confusion' }
3. 使用數(shù)據(jù)庫(kù)鏈接配置
var config = require('./config') ... const url = config.mongoUrl const connet = mongoose.connect(url, {useNewUrlParse: true, useCreateIndex: true})
4. 創(chuàng)建驗(yàn)證文件
./authenticate.js var passport = require('passport'), LocalStrategy = require('passport-local').Strategy, User = require('./models/user') var JwtStrategy = require('passport-jwt').Strategy, ExtractJwt = require('passport-jwt').ExtractJwt, jwt = require('jsonwebtoken') var config = require('./config.js') passport.use(new LocalStrategy(User.authenticate())) passport.serializeUser(User.serializeUser()) passport.deserializeUser(User.deserializeUser()) exports.getToken = function (user) { return jwt.sign(user, config.secretKey, {expiresIn: 3600}) // 簽發(fā)token時(shí)設(shè)置超時(shí)時(shí)間是3600s } var opts = {} opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken() // 從驗(yàn)證頭中提取,模型默認(rèn)是`'bearer'`. opts.secretOrKey = config.secretKey exports.jwtPassport = passport.use(new JwtStrategy(opts, (jwt_payload, done) => { console.log('JWT payload: ', jwt_payload) User.findOne({_id: jwt_payload._id}, (err, user) => { if (err) { return done(err, false) } else { if (user) { return done(null, user) } else { return done(null, false) } } }) })) exports.verifyUser = passport.authenticate('jwt', {session: false}) // 使用jwt就不再需要session保存用戶(hù)數(shù)據(jù)了。
5. 用戶(hù)申請(qǐng)登錄時(shí)把jwt給前端
// routes/users.js ... var authenticate = require('../authticate') router.post('/login', passport.authenticate('local'), (req, res) => { // 登錄時(shí)還是使用passport-local var token = authenticate.getToken({_id: req.user._id}) // 得到簽發(fā)后的jwt res.statusCode = 200 res.setHeader('Content-Type', 'application/json') res.json({success: true, token: token, status: 'You are successful logged in!'}) })
6. 前端保存token
// use localStorage $.ajax({ type: 'post', dataType: 'json', url: 'users/login', data: { username: 'un', password: 'pw' }, success: funciton (res) { localStorage.token = getToken(res) }, error: funciton (err) {...} }) // 還可以使用vux方法。 // 還可以使用封裝axios方法。
7. 用戶(hù)登錄超時(shí)
jsonwebtoken驗(yàn)證jwt后,若結(jié)果不通過(guò),會(huì)有3種錯(cuò)誤類(lèi)型。分別是
TokenExpiredError // 當(dāng)token超時(shí)時(shí)拋出。
err = { name: 'TokenExpiredError', massage: 'jwt expired', expired: [ExpDate] } JsonWebTokenError
jwt錯(cuò)誤
err = { name: 'JsonWebTokenError', message: 'jwt malformed' // 'jwt malformed', 'jwt signature in required', 'invalid signature', 'jwt audience invalid. expected: [OPTIONS AUDIENCE]', 'jwt issuer invalid. expected: [OPTIONS ISSUER]', 'jwt id invalid. expected:[OPTIONS JWT ID]', 'jwt subject invalid. expected: [OPTIONS SUBJECT]' }
NotBeforeError
當(dāng)當(dāng)前時(shí)間超過(guò)nbf的值時(shí)拋出該錯(cuò)誤。
err = { name: 'NotBeforeError', message: 'jwt not active', date: 2018-10-04T16:10:44.000Z }
passport在驗(yàn)證jwt不通過(guò)時(shí)(token過(guò)期也是一種不通過(guò))自動(dòng)向前端發(fā)送“狀態(tài)碼為401,內(nèi)容是Unauthorized”.
在使用passport/passport-jwt/jsonwebtoken時(shí)沒(méi)有發(fā)現(xiàn)處理token過(guò)期的方法。所以在使用passport-jwt驗(yàn)證不通過(guò)時(shí)再寫(xiě)一個(gè)驗(yàn)證是否過(guò)期的方法。
// authenicate.js ... export.verifyUser = passport.authenticate('jwt', { session: false, failureRedirect: '/error/auth' // 在這個(gè)路由里統(tǒng)一處理驗(yàn)證不通過(guò)的事情 })
// routes/error.js ... router.get('/auth', (req, res, next) => { let header = req.headers let rawToken = header.authorization if (!rawToken.split(' ').length) { res.json({ // 統(tǒng)一的數(shù)據(jù)結(jié)構(gòu)方便前端使用 code: 403, data: {}, message: 'error for get token' }) } else { let token = rawToken.split(' ')[1] jwt.verify(token, config.secretKey, err => { // 這里用到j(luò)sonwebtoken/config。注意引用 switch (err.name) { case 'TokenExpiredError': case 'NotBeforeError': let payload = jwt.decode(token) token = authenticate.getToken({_id: payload._id}) res.statusCode = 200 res.setHeader('Content-Type', 'application/json') res.json({success: true, token: token, status: '已經(jīng)刷新token'}) break case 'JsonWebTokenError': default: res.statusCode = 401 res.json({ code: 401, data: { error: err }, message: 'token錯(cuò)誤' }) break } }) } })
8. 用戶(hù)jwt驗(yàn)證不通過(guò)
passport在驗(yàn)證jwt不通過(guò)時(shí)(token過(guò)期也是一種不通過(guò))自動(dòng)向前端發(fā)送“狀態(tài)碼為401,內(nèi)容是Unauthorized”.
9. 用戶(hù)申請(qǐng)登出
在前端刪除token.
10. 不要打斷活動(dòng)用戶(hù)的操作
在no.7里若因?yàn)閠oken過(guò)期造成驗(yàn)證不通過(guò),則向前端返回了新的token。不是在不影響用戶(hù)操作前提下更新用戶(hù)的token的。下面在的總結(jié)的幾種不影響用戶(hù)操作的前提下更新用戶(hù)的token的方法。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。