JWT介绍
JWT全称JSON Web Token,就是登录成功后将相关用户信息组成 JSON 对象,然后对这个对象进行某种方式的加密,返回给客户端; 客户端在下次请求时带上这个 Token; 服务端再收到请求时校验 token 合法性,其实也就是在校验请求的合法性。
流程图解
代码实践
技术栈
- 前端:Vue2
- 后端:Koa + MongoDB
后端部分
1.安装npm i jsonwebtoken,在一个文件中配置
const jwt = require('jsonwebtoken')
const { secretKey, expiresIn } = require('../config/config')
// ../config/config中的内容
module.exports = {
secretKey: 'chrisWuHaHaHa', //密钥,用来解密token
expiresIn: 24 * 60 * 60 //过期时间
}
// 生成token
function generateToken(uid, scope,role) {
//第一个参数是保留在token的信息,可以用来鉴权
const token = jwt.sign(
{
uid,
role //user的角色权限
},
secretKey,
{
expiresIn
}
)
return token
}
module.exports = {
generateToken
}
2.在登录接口中生成token并返回给前端,user.role有两个角色admin和student
// 检测用户名、密码,返回 token 接口
tokenRouter.post('/', async ctx => {
// console.log(ctx.request);
const { username, password } = ctx.request.body;
let user = await findUser(username)
if (!user) {
ctx.body = {
errCode: 10001,
msg: '用户名或密码不正确',
}
return
}
const token = generateToken(user.id,user.role)
user.token = `Bearer ${token}`
ctx.body = {
user,token:`Bearer ${token}`
}
})
3.在需要登录下的请求,接口中可以jwt.verify利用判断
app.use(async (ctx, next) => {
const token = ctx.request.header.authorization.split(' ')[1];
const isValid = jwt.verify(token, secretKey);
if(isValid){
......
}else{
ctx.status = 401;
ctx.body = '登录已失效';
}
}
})
这里的isValid打印出来是
isValid {
uid: '6373a897b4edc7143c098ee2',
role: 'admin',
iat: 1668865922,
exp: 1668952322
}
到这一步JWT的鉴权已初步完成
但是设想一下JWT中的expiresIn只设置了10秒,用户每一次操作的间隔会很长,再想操作页面时cookie已过期,那又需要重新登录,这样体验感会很差😅😅
4.所以我们可以设置一个全局的中间件,在每次请求时更新token,重新发给前端
这个中间件需要放在路由前
app.use(async (ctx, next) => {
if(ctx.url.includes('token')){
//登录接口直接放行
await next();
}else{
// 前端在请求header.authorization中带上token
const token = ctx.request.header.authorization.split(' ')[1];
const isValid = jwt.verify(token, secretKey);
if(isValid){
// 生成新的token
const newToken = generateToken(isValid.uid,
isValid.role)
// 发给前端
ctx.set('Authorization',`Bearer ${newToken}`)
await next();
}else{
ctx.status = 401;
ctx.body = '登录已失效';
}
}
})
5.权限判断
一些接口需要admin权限才可以访问,所以我们可以写一个类,里面定义一个中间件判断权限
const jwt = require('jsonwebtoken')
const { secretKey } = require('../config/config')
class Auth {
get middleware() {
return async (ctx, next) => {
const token = ctx.request.header.authorization.split(' ')[1];
// 解密出token中的保留的信息
let result = jwt.verify(token, secretKey)
if (result.role === 'student') {
ctx.body = {
errCode: 1005,
msg: '权限不足',
}
return
}
await next()
}
}
}
module.exports = Auth
在接口中使用
contentRouter.post('/', new Auth().middleware, async ctx => {
ctx.body = '新增内容成功'
})
在这一步后端工作已全部完成了
前端部分
1.登录,将token储存到并存储到localStorage中
let result = await login(this.form);
// 拿到后端的token,并存储到localStorage中
localStorage.setItem('token', result.data.token);
2.axios的封装
//拦截请求
request.interceptors.request.use((config)=>{
let token = localStorage.getItem('token');
// 每次在请求头上带上token,登录接口时token是null,后端已放权
config.headers.Authorization = token;
return config
})
//拦截响应
request.interceptors.response.use((response)=>{
// 后端每次更新token会放在响应头中,所有重新保存在localStorage中
if(response.headers.authorization){
localStorage.setItem('token', response.headers.authorization)
}
return response
},function (error){
//对响应的错误做点什么
// console.log(error);
// 如果token过期,后端返回401状态码
if(error.response.status == 401){
router.push('/login')
}
// console.log(error.response)
return Promise.reject(error);
}
)
这一步前端的工作也完成了
END
- 整个前后端JWT鉴权+简单权限管理就全部实现了,相信看完session-cookie和jwt这两篇文章后,再也不怕面试官问到登录问题哈哈哈😃,接下来一篇打算后台权限管理的实现
- 希望这篇文章可以帮助到有需要的小伙伴,有问题可以在评论区留言或私信我