express框架中使用jwt實現(xiàn)驗證的方法
前言
接著上遍文章(使用session保存用戶數(shù)據(jù))來讓使用jwt保存用戶數(shù)據(jù)。
這里會用到passport-jwt/jsonwebtoken。
passport-jwt是passport的一個驗證策略。它使用jwt(json web token)驗證。
jsonwebtoken是一個編碼、解碼、驗證jwt的模塊。
使用jwt保存用戶數(shù)據(jù)與使用session保存用戶數(shù)據(jù)對比
| session | json web token | |
|---|---|---|
| 保存在server | 保存在client |
因session保存在server,所以服務(wù)器壓力比較大。聽說并發(fā)量達(dá)到1k時就能看到效果。
因jwt保存在client,所以需要加密。
使用jwt
1. 安裝依賴。
npm i passport-jwt jsonwebtoken
2. 創(chuàng)建一個配置文件,引用配置是使用。
// ./config.js
module.exports = {
secretKey: '12345-67890-9876-54321',
mongoUrl: 'mongodb://localhost:27017/confusion'
}
3. 使用數(shù)據(jù)庫鏈接配置
var config = require('./config')
...
const url = config.mongoUrl
const connet = mongoose.connect(url, {useNewUrlParse: true, useCreateIndex: true})
4. 創(chuàng)建驗證文件
./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è)置超時時間是3600s
}
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken() // 從驗證頭中提取,模型默認(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保存用戶數(shù)據(jù)了。
5. 用戶申請登錄時把jwt給前端
// routes/users.js
...
var authenticate = require('../authticate')
router.post('/login', passport.authenticate('local'), (req, res) => { // 登錄時還是使用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. 用戶登錄超時
jsonwebtoken驗證jwt后,若結(jié)果不通過,會有3種錯誤類型。分別是
TokenExpiredError // 當(dāng)token超時時拋出。
err = {
name: 'TokenExpiredError',
massage: 'jwt expired',
expired: [ExpDate]
}
JsonWebTokenError
jwt錯誤
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)前時間超過nbf的值時拋出該錯誤。
err = {
name: 'NotBeforeError',
message: 'jwt not active',
date: 2018-10-04T16:10:44.000Z
}
passport在驗證jwt不通過時(token過期也是一種不通過)自動向前端發(fā)送“狀態(tài)碼為401,內(nèi)容是Unauthorized”.
在使用passport/passport-jwt/jsonwebtoken時沒有發(fā)現(xiàn)處理token過期的方法。所以在使用passport-jwt驗證不通過時再寫一個驗證是否過期的方法。
// authenicate.js
...
export.verifyUser = passport.authenticate('jwt', {
session: false,
failureRedirect: '/error/auth' // 在這個路由里統(tǒng)一處理驗證不通過的事情
})
// 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錯誤'
})
break
}
})
}
})
8. 用戶jwt驗證不通過
passport在驗證jwt不通過時(token過期也是一種不通過)自動向前端發(fā)送“狀態(tài)碼為401,內(nèi)容是Unauthorized”.
9. 用戶申請登出
在前端刪除token.
10. 不要打斷活動用戶的操作
在no.7里若因為token過期造成驗證不通過,則向前端返回了新的token。不是在不影響用戶操作前提下更新用戶的token的。下面在的總結(jié)的幾種不影響用戶操作的前提下更新用戶的token的方法。
- 前端設(shè)置一個定時器。在小于過期時間時向后端請求新token并保存起來。
- 把token放在cookie時。后端從cookie里取出token,在過期前更新token。
- 將 token 存入 DB(如 Redis)中,失效則刪除;但增加了一個每次校驗時候都要先從 DB 中查詢 token 是否存在的步驟,而且違背了 JWT 的無狀態(tài)原則(這不就和 session 一樣了么?)。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
詳解nodejs http請求相關(guān)總結(jié)
這篇文章主要介紹了nodejs http請求相關(guān)總結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03
npm?install編譯時報"Cannot?read?properties?of?null?(r
這篇文章主要給大家介紹了關(guān)于npm?install編譯時報“Cannot?read?properties?of?null?(reading?‘pickAlgorithm‘)“錯誤的解決辦法,文中將解決方法介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
Nodejs監(jiān)控事件循環(huán)異常示例詳解
這篇文章主要給大家介紹了關(guān)于Nodejs監(jiān)控事件循環(huán)異常的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Nodejs具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Node.js與Sails ~項目結(jié)構(gòu)與Mvc實現(xiàn)及日志機制
Sails是一個Node.js的中間架構(gòu),很方便的幫助我們搭建web應(yīng)用程序。還有node.js與Sails日志機制在本文中也講到了,需要的朋友可以一起學(xué)習(xí)下2015-10-10
Linux系統(tǒng)中如何下載、解壓和安裝特定版本的Node.js
Nodejs版本坑眾多,不同應(yīng)用可能需要不同版本,下面這篇文章主要給大家介紹了關(guān)于Linux系統(tǒng)中如何下載、解壓和安裝特定版本的Node.js的相關(guān)資料,需要的朋友可以參考下2024-01-01

