likes
comments
collection
share

全栈项目开发——NOTEBOOK(6):使用JWT生成token实现后端鉴权

作者站长头像
站长
· 阅读数 18

前言

上篇文章中我们了解了什么是鉴权什么是路由守卫,并且成功通过前置守卫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 大概就像下面这样。

全栈项目开发——NOTEBOOK(6):使用JWT生成token实现后端鉴权

它是一个很长的字符串,中间用点(.)分隔成三个部分

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结构。

全栈项目开发——NOTEBOOK(6):使用JWT生成token实现后端鉴权

6. token的组成结构

- Header :包含令牌类型(token的类型typ)和签名算法alg ,
- Payload :包含需要传递的数据。如用户信息和元数据,如用户ID、过期时间等 
- Signature : 通过私钥和特定算法对HeaderPayload的加密签名

在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,usernameadmin , 我们检验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

恭喜你!成功实现了高效的鉴权机制!

全栈项目开发——NOTEBOOK(6):使用JWT生成token实现后端鉴权

转载自:https://juejin.cn/post/7399587895369465871
评论
请登录