使用jwt实现express框架下的校验。然后通过文章(使用session保存用户数据)使用jwt保存用户数据。这里将使用passport-jwt/jsonwebtoken。passport-jwt是passport的验证策略。它使用jwt(json网络令牌)身份验证。jsonwebtoken是jwt编码、解码、验证的模块。使用jwt保存用户数据与使用session保存用户数据相比。sessionjson网络令牌保存在服务器上并保存在客户端上。因为session是保存在服务器上的,服务器压力比较大。听说并发到1k才能看到效果。因为jwt保存在客户端,所以需要加密。使用jwt1。安装依赖项。npmipassport-jwtjsonwebtoken2。创建配置文件,使用参考配置。//./config.jsmodule.exports={secretKey:'12345-67890-9876-54321',mongoUrl:'mongodb://localhost:27017/confusion'}3.使用数据库链接配置varconfig=require('./config')...consturl=config.mongoUrlconstconnet=mongoose.connect(url,{useNewUrlParse:true,useCreateIndex:true})4.创建认证文件./authenticate.jsvarpassport=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.secretKeyexports.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-localvartoken=authenticate.getToken({_id:req.user._id})//获取发布的jwtres.statusCode=200res.setHeader('Content-Type','application/json')res.json({success:true,token:token,status:'您登录成功!'})})6.在前端保存令牌//使用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//在令牌过期时抛出。err={name:'TokenExpiredError',message:'jwtexpired',expired:[ExpDate]}JsonWebTokenErrorjwterrorerr={name:'JsonWebTokenError',message:'jwtmalformed'//'jwtmalformed','jwt签名在必需','无效签名','jwt观众无效。预期:[OPTIONSAUDIENCE]','jwt发行人无效。预期:[OPTIONSISSUER]','jwtid无效。预期:[OPTIONSJWTID]','jwt主题无效。expected:[OPTIONSSUBJECT]'}NotBeforeError当当前时间超过nbf的值时抛出这个错误。err={name:'NotBeforeError',message:'jwtnotactive',date:2018-10-04T16:10:44.000Z}passport在jwt验证失败时自动发送给前端(token过期也是失败)“状态码为401,内容为未授权”。使用passport/passport-jwt/jsonwebtoken时,没有办法处理token过期。所以在使用passport-jwt验证失败的时候,写一个验证是否过期的方法。//authenticate.js...export.verifyUser=passport.authenticate('jwt',{session:false,failureRedirect:'/error/auth'//本路由验证失败统一处理})//routes/error.js...router.get('/auth',(req,res,next)=>{letheader=req.headersletrawToken=header.authorizationif(!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=200res。setHeader('Content-Type','application/json')res.json({success:true,token:token,status:'tokenrefreshed'})breakcase'JsonWebTokenError':default:res.statusCode=401res.json({code:401,data:{error:err},message:'tokenerror'})break}})}})8.passport验证jwt失败时用户jwt验证失败(token过期也是Fail)自动向前端发送“状态码为401,内容为未授权”。9.用户申请注销,前端删除token。10.不要打断活跃用户的操作。如果通过,则向前端返回一个新的令牌。不是在不影响用户操作的情况下更新用户的token。下面总结几种在不影响用户操作的情况下更新用户的token的方法。前端设置一个定时器。当小于过期时间时,向后端请求新的token并保存。将令牌放入cookie时。后端从cookie中取出token,并在token过期前更新token。将token存放在DB(如Redis)中,失败时删除;但它增加了一个步骤,每次验证时都要从DB中查询token是否存在,违背了JWT的无状态原则(这和Isthesessionsame一样吗?)。
