• <legend id="m0ego"></legend>
  • <td id="m0ego"></td>
  • express框架中使用jwt實現驗證的方法

    文章TAG:驗證 jwt express
    時間:2019-08-25來源/作者:飛機 編輯:源碼庫 文章熱度:

    前言

    接著上遍文章(使用session保存用戶數據)來讓使用jwt保存用戶數據。

    這里會用到passport-jwt/jsonwebtoken。

    passport-jwt是passport的一個驗證策略。它使用jwt(json web token)驗證。

    jsonwebtoken是一個編碼、解碼、驗證jwt的模塊。

    使用jwt保存用戶數據與使用session保存用戶數據對比

     

    session json web token  
    保存在server 保存在client  

     

    因session保存在server,所以服務器壓力比較大。聽說并發量達到1k時就能看到效果。

    因jwt保存在client,所以需要加密。

    使用jwt

    1. 安裝依賴。

    npm i passport-jwt jsonwebtoken

    2. 創建一個配置文件,引用配置是使用。

    // ./config.js
    module.exports = {
     secretKey: '12345-67890-9876-54321',
     mongoUrl: 'mongodb://localhost:27017/confusion'
    }

    3. 使用數據庫鏈接配置

    var config = require('./config')
    ...
    const url = config.mongoUrl
    const connet = mongoose.connect(url, {useNewUrlParse: true, useCreateIndex: true})

    4. 創建驗證文件

    ./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}) // 簽發token時設置超時時間是3600s
    }
    
    var opts = {}
    opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken() // 從驗證頭中提取,模型默認是`'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保存用戶數據了。

    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}) // 得到簽發后的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后,若結果不通過,會有3種錯誤類型。分別是

    TokenExpiredError // 當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

    當當前時間超過nbf的值時拋出該錯誤。

    err = {
     name: 'NotBeforeError',
     message: 'jwt not active',
     date: 2018-10-04T16:10:44.000Z
    }

    passport在驗證jwt不通過時(token過期也是一種不通過)自動向前端發送“狀態碼為401,內容是Unauthorized”.
    在使用passport/passport-jwt/jsonwebtoken時沒有發現處理token過期的方法。所以在使用passport-jwt驗證不通過時再寫一個驗證是否過期的方法。

    // authenicate.js
    ...
    export.verifyUser = passport.authenticate('jwt', {
     session: false,
     failureRedirect: '/error/auth' // 在這個路由里統一處理驗證不通過的事情
     })
    // routes/error.js
    ...
    router.get('/auth', (req, res, next) => {
     let header = req.headers
     let rawToken = header.authorization
     if (!rawToken.split(' ').length) {
     res.json({ // 統一的數據結構方便前端使用
     code: 403,
     data: {},
     message: 'error for get token'
     })
     } else {
     let token = rawToken.split(' ')[1]
     jwt.verify(token, config.secretKey, err => { // 這里用到jsonwebtoken/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: '已經刷新token'})
      break
     case 'JsonWebTokenError':
     default:
      res.statusCode = 401
      res.json({
      code: 401,
      data: {
      error: err
      },
      message: 'token錯誤'
      })
      break
     }
     })
     }
     })

    8. 用戶jwt驗證不通過

    passport在驗證jwt不通過時(token過期也是一種不通過)自動向前端發送“狀態碼為401,內容是Unauthorized”.

    9. 用戶申請登出

    在前端刪除token.

    10. 不要打斷活動用戶的操作

    在no.7里若因為token過期造成驗證不通過,則向前端返回了新的token。不是在不影響用戶操作前提下更新用戶的token的。下面在的總結的幾種不影響用戶操作的前提下更新用戶的token的方法。

    1. 前端設置一個定時器。在小于過期時間時向后端請求新token并保存起來。
    2. 把token放在cookie時。后端從cookie里取出token,在過期前更新token。
    3. 將 token 存入 DB(如 Redis)中,失效則刪除;但增加了一個每次校驗時候都要先從 DB 中查詢 token 是否存在的步驟,而且違背了 JWT 的無狀態原則(這不就和 session 一樣了么?)。

    總結

    以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對ASPKU源碼庫的支持。


    注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
    相關JavaScript/Ajax教程
    熱門標簽

    JavaScript/Ajax教程Rss訂閱JavaScript/Ajax教程搜索