express框架中使用jwt实现验证的方法
前言
接着上遍文章(使用session保存用户数据)来让使用jwt保存用户数据。
这里会用到passport-jwt/jsonwebtoken。
passport-jwt是passport的一个验证策略。它使用jwt(jsonwebtoken)验证。
jsonwebtoken是一个编码、解码、验证jwt的模块。
使用jwt保存用户数据与使用session保存用户数据对比
| session | jsonwebtoken | |
|---|---|---|
| 保存在server | 保存在client |
因session保存在server,所以服务器压力比较大。听说并发量达到1k时就能看到效果。
因jwt保存在client,所以需要加密。
使用jwt
1.安装依赖。
npmipassport-jwtjsonwebtoken
2.创建一个配置文件,引用配置是使用。
//./config.js
module.exports={
secretKey:'12345-67890-9876-54321',
mongoUrl:'mongodb://localhost:27017/confusion'
}
3.使用数据库链接配置
varconfig=require('./config')
...
consturl=config.mongoUrl
constconnet=mongoose.connect(url,{useNewUrlParse:true,useCreateIndex:true})
4.创建验证文件
./authenticate.js
varpassport=require('passport'),
LocalStrategy=require('passport-local').Strategy,
User=require('./models/user')
varJwtStrategy=require('passport-jwt').Strategy,
ExtractJwt=require('passport-jwt').ExtractJwt,
jwt=require('jsonwebtoken')
varconfig=require('./config.js')
passport.use(newLocalStrategy(User.authenticate()))
passport.serializeUser(User.serializeUser())
passport.deserializeUser(User.deserializeUser())
exports.getToken=function(user){
returnjwt.sign(user,config.secretKey,{expiresIn:3600})//签发token时设置超时时间是3600s
}
varopts={}
opts.jwtFromRequest=ExtractJwt.fromAuthHeaderAsBearerToken()//从验证头中提取,模型默认是`'bearer'`.
opts.secretOrKey=config.secretKey
exports.jwtPassport=passport.use(newJwtStrategy(opts,(jwt_payload,done)=>{
console.log('JWTpayload:',jwt_payload)
User.findOne({_id:jwt_payload._id},(err,user)=>{
if(err){
returndone(err,false)
}else{
if(user){
returndone(null,user)
}else{
returndone(null,false)
}
}
})
}))
exports.verifyUser=passport.authenticate('jwt',{session:false})//使用jwt就不再需要session保存用户数据了。
5.用户申请登录时把jwt给前端
//routes/users.js
...
varauthenticate=require('../authticate')
router.post('/login',passport.authenticate('local'),(req,res)=>{//登录时还是使用passport-local
vartoken=authenticate.getToken({_id:req.user._id})//得到签发后的jwt
res.statusCode=200
res.setHeader('Content-Type','application/json')
res.json({success:true,token:token,status:'Youaresuccessfulloggedin!'})
})
6.前端保存token
//uselocalStorage
$.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:'jwtexpired',
expired:[ExpDate]
}
JsonWebTokenError
jwt错误
err={
name:'JsonWebTokenError',
message:'jwtmalformed'//'jwtmalformed','jwtsignatureinrequired','invalidsignature','jwtaudienceinvalid.expected:[OPTIONSAUDIENCE]','jwtissuerinvalid.expected:[OPTIONSISSUER]','jwtidinvalid.expected:[OPTIONSJWTID]','jwtsubjectinvalid.expected:[OPTIONSSUBJECT]'
}
NotBeforeError
当当前时间超过nbf的值时抛出该错误。
err={
name:'NotBeforeError',
message:'jwtnotactive',
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)=>{
letheader=req.headers
letrawToken=header.authorization
if(!rawToken.split('').length){
res.json({//统一的数据结构方便前端使用
code:403,
data:{},
message:'errorforgettoken'
})
}else{
lettoken=rawToken.split('')[1]
jwt.verify(token,config.secretKey,err=>{//这里用到jsonwebtoken/config。注意引用
switch(err.name){
case'TokenExpiredError':
case'NotBeforeError':
letpayload=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一样了么?)。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。