全栈项目开发——NOTEBOOK(6):使用JWT生成token实现后端鉴权
前言
上篇文章中我们了解了什么是鉴权什么是路由守卫,并且成功通过前置守卫router.beforeEach实现前端鉴权。但是新的问题接着出现——如何防止用户直接在浏览器后台插入userInfo实现免注册登入跳转到目标页面?
一, 后端鉴权
这就需要实现后端鉴权,什么是后端鉴权?在上一篇文章中我们已经介绍过前后端鉴权方式,后端鉴权主要有以下几步:
前端输入正确账号密码给到后端
后端校验账号密码合法,用该账号信息生成一个加密的令牌token,并返回给前端保存
前端接下来所有的接口请求全部需要带上这个接口请求token给后端
后端校验token是否合法,如果合法,则登入成功
由此可见我们已经实现了第一步,下一步我们需要将前端传递的账号密码进行加密,生成token令牌。我们需要借助JWT来生成token令牌。
二, token生成工具——JWT
什么是JWT
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案
JWT是JSON Web Tokens的缩写,是一种高效且安全的令牌标准,专用于在双方间安全地传输信息。它巧妙地将用户信息以JSON格式封装,并通过数字签名进行加密,确保信息传输过程中的完整性和真实性。JWT广泛应用于身份验证场景,支持跨域认证,同时无需服务器间共享会话状态,极大地简化了认证流程与系统设计。
JSON Web Token 入门教程 - 阮一峰的网络日志 (ruanyifeng.com)
点击阮一峰老师的网络日志进行 JWT 的学习。
JWT 的原理
JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户就像下面这样。
{ "姓名": "张三", "角色": "管理员", "到期时间": "2018年7月1日0点0分" }
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。
JWT 的数据结构
实际的 JWT 大概就像下面这样。
它是一个很长的字符串,中间用点(.
)分隔成三个部分
JWT 的三个部分依次如下。
- Header(头部)
- Payload(负载)
- Signature(签名)
写成一行,就是下面的样子。
Header.Payload.Signature
三,使用JWT生成token
了解JWT后,我们使用JWT来生成token。
在npm | Home (npmjs.com)中搜索jsonwebtoken查看JWT的使用方法
1 .在后端下载依赖包
npm i jsonwebtoken
2. 新建utils文件夹
utils
文件夹主要用于存放工具函数和辅助方法。新建utils文件夹,在里面新建jwt.js文件,引入jsonwebtoken库,实例化
const jwt = require('jsonwebtoken')
3. 封装sign方法
封装sign方法,接收用户信息和签名,设置过期时间,超过这个时间令牌过期,用户将需要重新登入。同时可以传入我们的署名,在这里我取为 '绵绵冰最帅'
function sign(option) {
return jwt.sign(option, '绵绵冰最帅', {
expiresIn: 86400 // 一天后后期
})
}
// 抛出
module.exports = {
sign
}
4. 使用抛出的sign方法生成token
后端的登入验证页面如果成功拿到用户输入,将用户输入传入sign生成token。
if (result.length) {
let data = {
id: result[0].id,
nickname: result[0].nickname,
username: result[0].username
}
// 生成token
// 调用了抛出的sign方法,传入了参数,设置了管理员权限
let token = sign({
id: result[0].id,
username: result[0].username, admin: true
})
// 打印检测是否成功生成
console.log(token);
} else {...}
5. 填写登入界面,返回后在node.js中得到token
在Node.js中得到这个Token意味着服务器已经确认了用户的身份。在后续的请求中都将使用这个Token来验证用户的身份和权限,在token生效时间内,无需每次都重新验证用户名和密码。客户端可以在后续请求中携带这个Token,以安全地访问受保护的资源。
得到的token如下,如图所示,符合我们上文提到的Header.Payload.Signature结构。
6. token的组成结构
- Header :包含令牌类型(token的类型typ)和签名算法alg ,
- Payload :包含需要传递的数据。如用户信息和元数据,如用户ID、过期时间等
- Signature : 通过私钥和特定算法对Header和Payload的加密签名
在JWT认证中,用户登录后,服务器验证身份后构造包含用户信息的Payload,并使用密钥生成Signature。Header、Payload、Signature组成JWT(token)返回客户端。客户端在后续请求中携带JWT,服务器验证JWT确认用户身份及权限。
四,请求拦截
后端生成JWT token后,将其发送给前端。前端存储此token于本地(如浏览器LocalStorage)。为确保后续请求的身份验证,前端需要设置请求拦截器,每当请求发送时(用户登入时)自动在每个请求头中加入token。后端接收请求时,校验token有效性,从而验证用户身份。
1 .将token传给前端
在后端成功接收用户信息后将用户数据和token传给前端
// 登入成功
ctx.body = {
data: data,
code: '8000',
msg: '登录成功',
token: token
}
2. 前端将后端传递过来的token存储到浏览器本地
// 将后端传递过来的token存储到浏览器本地
localStorage.setItem('token', res.token)
3. 请求拦截
用户的每次登入,触发的接口请求全部需要带上token给后端校验。我们需要在请求发送前用 axios 中的 interceptors 方法拦截请求 request ,在使用 use 针对相应数据进行处理。
请求拦截
-
请求拦截:请求发送前对请求进行一些预处理操作
-
检查 localStorage 中是否存在 token,并将其添加到请求的头部
-
使用 Axios 库中的
axios.interceptors.request.use
方法添加请求拦截器 -
返回被处理(添加token)后的请求体
// 使用axios中的方法添加请求拦截器
axios.interceptors.request.use(req => {
let jwtToken = window.localStorage.getItem('token')
if (jwtToken) {
// 存放在请求头中
req.headers.Authorization = jwtToken
}
return req
})
五,后端测试前端传递过来的token是否合法
在utils
文件夹jwt.js文件中打造函数verify()
进行校验。
1. 拿到token
拿到ctx中的 req.headers.authorization ,他是由前端请求拦截处理后发送的数据,authorization即为token,我们在ctx中拿到他
2. 判断token的合法性
使用 jwt.verify() , 传入待验证的token和我们在sign函数中构造token时传入的签名 '绵绵冰最帅' 。 还记得我们在生成token时传入了三个参数吗? id,username 和 admin , 我们检验token中的id来判断token是否是能提供登入的合法 id , 如果该id合法就直接 next() 放行
3. 将错误原因返回给前端
如果登入失败,返回状态码 status 401 和 msg 告诉前端token无效。
// 检验前端传递的token
function verify() {
return async (ctx, next) => {
// 拿到前端发送的请求头里的token
let jwtToken = ctx.req.headers.authorization
if (jwtToken) {
// 判断 token 是否合法
try {
const decoded = jwt.verify(jwtToken, '绵绵冰最帅')
if (decoded.id) { // 合法
next()
}
} catch (e) {
ctx.body = {
msg: 'token失效',
status: 401
}
}
// 第一次登入token为undefind
} else {
ctx.body = {
msg: '请提供token',
status: 401
}
}
}
}
4. 响应拦截
前端需要再次进行响应拦截的鉴权,多一层对后端返回的状态码res.data.status的判断,将错误原因通过轻提示返回给用户
在响应拦截中做出修改: 如果 token 无效 ,即res.data.status === 401
时将页面跳转至登入页面 ,并给出轻提示
/ 2 .响应拦截:服务器响应后对响应进行后处理的过程
axios.interceptors.response.use(res => {
if (res.status !== 200) { // 程序错误
showToast('服务器异常');// 轻提示
return Promise.reject(res) // 弹出
} else {
// 当前端传递的 token 突然失效时
if (res.data.status === 401) { // 登录失效
showToast(res.data.msg); // 轻提示
router.push('/login') // 跳转
return Promise.reject(res) // 弹出
}
// 接口成功,并返回了数据,判断是否是空数组:请求数据失败?拿到空数组
if (res.data.code !== '8000') { // 逻辑性错误
showToast(res.data.msg); // 弹出后端返回的信息
return Promise.reject(res) // 将这个错误弹出
}
return res.data // 返回数据
}
})
小计
本文深入探讨了后端鉴权机制,特别是JWT(JSON Web Tokens)的应用。通过使用jsonwebtoken
库生成安全的token,并在前后端间通过请求拦截器传递,实现了token的无缝流通。后端则通过对token的合法性和有效性做出判断,确保了请求的安全性,从而实现了高效的鉴权机制。这一过程不仅增强了系统的安全性,还提升了用户体验,使鉴权逻辑更加严谨和高效。
本章要点:后端鉴权,JWT , token(jsonwebtoken) , 封装sign()方法 , 封装verify()方法 , 请求拦截, getItem(), req.headers.Authorization ,ctx.req.headers.authorization
恭喜你!成功实现了高效的鉴权机制!
转载自:https://juejin.cn/post/7399587895369465871