前言
接着上遍文章(使用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的方法。
- 前端设置一个定时器。在小于过期时间时向后端请求新token并保存起来。
- 把token放在cookie时。后端从cookie里取出token,在过期前更新token。
- 将 token 存入 DB(如 Redis)中,失效则删除;但增加了一个每次校验时候都要先从 DB 中查询 token 是否存在的步骤,而且违背了 JWT 的无状态原则(这不就和 session 一样了么?)。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]