token的作用
token
和曾经cookie、session
的作用基本一样,可以解决http
无状态的问题,在token中我们可以存储一些信息,这些信息就是浏览器和服务器沟通后留下的一些状态记录。我们可以在里面存储关于用户的一些信息来做自动登录功能或者使用它来证明用户的身份来做一些权限方面的东西。
jsonwebtoken的使用
const {sign,verify}=require('jsonwebtoken')
// sign(payload, secretOrPrivateKey, [options]) 签发token
/*
payload 需要加密的内容
secretOrPrivateKey 加密所用的字符串或者private.key密钥文件
options:{
algorithm: 'HS256', // 加密使用的算法如果不使用private.key 只能使用HS开头的算法
expiresIn:'10s' 有效时间,若加密内容不是对象则无法设置,有效时间可以是数字、字符串 如 60, "2 days", "10h", "7min".
}
*/
const token=sign({name:'xxx'},'secret',{ algorithm: 'HS256',expiresIn:'1h'})
// jwt.verify(token, secretOrPublicKey,callback) 解密token
/*
token 签发得到的token
secretOrPublicKey 签发token时的密钥
callback (err,res)=>{},err是错误对象,res是解密的结果
*/
const verifyResult=verify(token,'secret',(err,data)=>{})
项目中如何使用token
1.在用户访问涉及敏感信息的页面或者说这个页面是要基于用户信息来展示的,此时我们需要重定向到登录页让用户登录。或者说你的项目只有在登录后才能加入主界面,看项目具体需求吧。
2.当用户登录后,服务器会为用户签发一个token,然后会把token返回给前端。之后当发送的请求涉及敏感信息的话,那我们需要携带token去发送这个请求,然后服务器会对这个token进行一个验证再去处理这次请求,我们会根据服务器的反馈来决定如何处理用户的这个操作。
3.当用户登录后,我们会把token存储在浏览器缓存和vuex中。如果用户关闭浏览器该标签页后,再次访问这个网页时,我们可以读取token做自动登录功能。
示例演示
服务端代码(express+mongoose)
先封装一个验证token的中间件
const {Users}=require('../db/tables') // 获取用户表
const jwt=require("jsonwebtoken")
const {codekey}=require("../tools/const") // 获取密钥
module.exports =(req,res,next)=>{
// 把不需要携带token的请求过滤掉
if(req.url=="/login"||"/register"){next(); return}
const token=req.get('authorization') // 获取token
if(token)
jwt.verify(token,codekey,(err,data)=>{
if(data){ // 如果成功解密data是一个真值
// 通过解密出来的用户id去数据库查找用户
Users.findOne({_id:data._id})
.then((doc)=>{if(doc)next();else res.status(401).send({msg:'not valid token'})}) // 如果查找到用户,说明token是有效的。如果token是无效的,应该响应无权
.catch(()=>{res.status(500).send('server err')})
}
else res.status(401).send({msg:'not valid token'}) //如果解密失败说明token也无效
})
else res.status(401).send({msg:'have not token'})
}
再看路由接口的代码
const jwt=require('jsonwebtoken')
const {codekey}=require("../tools/const") //获取密钥
// 使用上面封装的中间件,用来验证用token
server.use(require("./tools/verifyToken"))
// 处理登录请求
router.post('/login',(req,res)=>{
const user=req.body // 获取前端传过来的用户信息
Users.findOne(user) // 查找该用户
.then((doc)=>{
if(doc){
const token=jwt.sign({_id:doc._id},codekey,{algorithm:"HS512",expiresIn:'1 h'})
res.send({status:1,token}) //登录通过并把token返回给前端
}
else res.send({status:2}) // 登录信息有误
})
.catch(()=>{res.status(500).send('server err')}) // 服务器错误
})
// 获取用户信息的请求
router.get('/userinfo',(req,res)=>{
const {email}=req.query
Users.findOne({email})
.then((doc)=>{delete doc.psd;res.send({status:1,userinfo:doc})})
.catch(()=>{res.status(500).send('server err')})
})
前端代码
封装一个发送请求的模块(axios)
import axios from "axios"
import {Toast} from 'vant' // 引入轻提示组件
import getuser from "../tools/getuser" // 引入获取当前用户信息的方法
import vm from "../main" // 获取vue实例
const store=vm.$store // 获取vuex
let cancelreq // 用来存储取消本次请求的函数
// 请求拦截
axios.interceptors.request.use((config)=>{
// 过滤掉不需要携带token的请求
if(config.url==='login'||"register") return config
config.cancelToken=new axios.CancelToken((c)=>{cancelreq=c})
const token=getuser().token // 读取该用户的token
if(!token) // 如果没有token取消本次请求然后重定向到登录页
{
cancelreq('cancel') // 下面响应拦截的的err 为 {message:cancel}
//cancelReq({reason:'cancel'}) 下面响应拦截的的err 为 {message:{reason:cancel}}
store.dispatch('logout') // 在vuex中完成了退出登录的逻辑
}
else // 否则携带token发送请求
config.headers.authorization=token // 这里要注意了,如果getuser()是null,这里不会报错,而是会在响应拦截处报错
return config
})
// 响应拦截
axios.interceptors.response.use((res)=>{
return res.data //200,一切正常
},
(err)=>{
if(err.response) // 如果能接收到服务器的响应
{
// 根据服务器响应的状态码来统一处理响应。根据接口文档来写
if(err.response.status===401) // 状态码为401说明token无效,此时重定向到登录页
{
Toast.fail({duration:3000,position:top,message:'身份过期,请重新登录!'})
store.dispatch('logout') // 在vuex中完成了退出登录的逻辑(重置用户信息并且完成页面的跳转)
return {status:401}
}
// 状态码为404或者500说明是后端的问题
else if(err.response.status===500||err.response.status===404)
{
Toast.fail({position:top,message:'服务器繁忙,请稍后再试!'})
return {status:500}
}
}
else // 不能接收到服务器的响应,说明网络异常。或者是取消了请求
{
if(err.message==='cancel')
{
store.dispatch('logout')
Toast.fail({position:top,message:'请先登录!'})
return {status:-2}
}
Toast.fail({position:top,message:'网络异常,请稍后再试!'})
return {status:-1}
}
}
)
// 配合上面的拦截封装post请求的方法
function postreq(requrl,body={}){
return axios({
method:"POST",
// baseURL:'',
url:requrl,
data:body,
timeout:5000
})
}
// 配合上面的拦截封装get请求的方法
function getreq(requrl,query={}){
return axios({
// baseURL:'',
url:requrl,
params:query,
timeout:5000
})
}
export {postreq,getreq}
创建文件api.js
,里面写所有接口
import {postreq,getreq} from "./req"
//注册请求
export const RegisterReq=(info={})=>postreq("/register",info)
//登录请求
export const LoginReq=(info={})=>postreq("/login",info)
//获取用户信息的请求
export const GetUserInfoReq=(info={})=>getreq('/userinfo',info)
路由页面-登录页login.vue
import {LoginReq} from "api.js" // 引入接口
created(){
// 自动登录。这里不需要检验,因为登陆进入主界面后,肯定会发请求,如果token无效,请求拦截会去处理
if(getuser().token) this.$store.dispatch('login')
}
methods:{
async login(){
const res=await LoginReq({email,psd})
if(res.status==1) // 为1代表用户的登录信息正确
this.$store.dispatch('login') // 在vuex中完成登录的逻辑(存储用户信息和 token,并且完成页面的跳转)
}
}
路由页面-个人中心页personCenter.vue
import {GetUserInfoReq} from "api.js" // 引入接口
import import getuser from "../tools/getuser" // 引入获取用户信息的方法
async created(){
const res=await GetUserInfoReq({email:getuser().email})
}
结尾
上面大概就是token
在项目中的使用方法,大概就是这么一个业务。具体情况根据项目需求来写。如果你理解了上面这个业务,相信你对token
一定有了一个很好的了解,希望这篇文章能给你带来快乐。如果有小伙伴有问题或者疑惑,欢迎提出和分享。