Node.js 中如何进行 JWT(JSON Web Tokens)身份验证和授权

目录

[JWT 使用场景](#JWT 使用场景)

[JWT 基本概念](#JWT 基本概念)

常用方法

[1. 生成 JWT](#1. 生成 JWT)

[2. 验证 JWT](#2. 验证 JWT)

[3. 中间件](#3. 中间件)

实践案例

项目实战

封装jwt文件

[后端登录接口生成token 设置为响应头](#后端登录接口生成token 设置为响应头)

全局路由中间件验证token

前端拦截处理token

提示、技巧和注意事项


JWT 是一种用于安全传输信息的开放标准,它通常用于身份验证和授权。在 Node.js 中,你可以使用库如jsonwebtoken来创建和验证 JWT。JWT 允许你在服务器和客户端之间安全地传递信息,而无需存储会话状态。这使得 JWT 非常适合构建分布式系统,如单页应用(SPA)、移动应用和微服务。

JWT 使用场景

在什么情况下使用 JWT 是有意义的?以下是一些常见的使用场景:

  1. 用户身份验证: JWT 可用于验证用户的身份。当用户登录时,服务器可以为其生成一个 JWT,客户端可以在后续请求中发送这个 JWT 来证明其身份。
  2. 单点登录(SSO): JWT 可以用于实现单点登录,让用户一次登录即可访问多个相关应用,而无需重复登录。
  3. API 授权: 在 API 调用中,JWT 可以包含授权信息,以便在服务器端验证用户是否有权限执行特定操作。
  4. 密码重置: JWT 可用于安全地生成包含重置密码令牌的链接,以允许用户重置其密码。
  5. 移动应用认证: 移动应用可以使用 JWT 来与后端服务器进行身份验证,确保只有经过授权的用户可以访问后端资源。

JWT 基本概念

Node.js 中使用 JWT,你需要了解以下基本概念:

  1. JWT 结构: JWT 由三部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature)。头部包含算法和令牌类型,载荷包含要传递的信息,签名用于验证令牌的真实性。
  2. 密钥: 生成和验证 JWT 时,你需要一个密钥。密钥可以是对称密钥(使用相同的密钥进行签名和验证)或非对称密钥(使用不同的密钥进行签名和验证)。
  3. 签名验证: 接收 JWT 的服务器使用密钥验证签名以确保 JWT 未被篡改。如果签名验证成功,服务器可以信任 JWT 中的信息。

常用方法

以下是在 Node.js 中进行 JWT 身份验证和授权的常用方法:

1. 生成 JWT

使用jsonwebtoken库可以生成 JWT。首先,你需要安装该库:

npm install jsonwebtoken

然后,你可以使用以下代码生成 JWT:

const jwt = require('jsonwebtoken');

const payload = { userId: 123, role: 'admin' };
const secretKey = 'your-secret-key';

const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });

2. 验证 JWT

服务器收到 JWT 后,需要验证其真实性。使用以下代码验证 JWT:

const jwt = require('jsonwebtoken');

const token = 'your-jwt-token';
const secretKey = 'your-secret-key';

try {
  const decoded = jwt.verify(token, secretKey);
  console.log(decoded);
} catch (error) {
  console.error('JWT verification failed');
}

3. 中间件

在 Express.js 中,你可以创建 JWT 验证中间件来保护特定路由。以下是一个示例:

const jwt = require('jsonwebtoken');
const secretKey = 'your-secret-key';

function authenticateToken(req, res, next) {
  const token = req.header('Authorization');
  if (!token) return res.status(401).send('Access denied');

  try {
    const decoded = jwt.verify(token, secretKey);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(403).send('Invalid token');
  }
}

实践案例

让我们通过一个实际案例来演示如何在 Node.js 中进行 JWT 身份验证和授权。我们将创建一个简单的 Express.js 应用,并使用 JWT 来保护一个受限的路由。

const express = require('express');
const jwt = require('jsonwebtoken');
const secretKey = 'your-secret-key';

const app = express();
app.use(express.json());

// 登录路由,生成JWT
app.post('/login', (req, res) => {
  const { username, password } = req.body;

  // 在实际应用中,这里会验证用户名和密码
  if (username === 'user' && password === 'password') {
    const payload = { username };
    const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
    res.json({ token });
  } else {
    res.status(401).json({ error: 'Authentication failed' });
  }
});

// 受保护的路由,需要JWT验证
app.get('/protected', authenticateToken, (req, res) => {
  res.json({ message: 'This is a protected route', user: req.user });
});

function authenticateToken(req, res, next) {
  const token = req.header('Authorization');
  if (!token) return res.status(401).send('Access denied');

  try {
    const decoded = jwt.verify(token, secretKey);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(403).send('Invalid token');
  }
}

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

在上面的示例中,我们创建了一个登录路由,生成 JWT 并将其返回给客户端。然后,我们创建了一个受保护的路由,使用authenticateToken中间件来验证 JWT。如果 JWT 验证成功,用户将能够访问受保护的路由。

项目实战

封装jwt文件

const jsonwebtoken = require("jsonwebtoken");
const secrcet = "贾公子";
const JWT = {
  // 生成token
  generate(value, exprires) {
    // 加密内容 密钥 过期时间
    return jsonwebtoken.sign(value, secrcet, { expiresIn: exprires });
  },
  // token 解密
  verify(token) {
    try {
      return jsonwebtoken.verify(token, secrcet);
    } catch (error) {
      return false;
    }
  },
};

module.exports = JWT;

后端登录接口生成token 设置为响应头

   login: async (req, res) => {
        console.log(req.body);
        // req.body
        var result = await UserService.login(req.body)
        if (result.length == 0) {
            res.send({
                code: '-1',
                error: '用户名密码不匹配'
            })
        } else {
            // 生成token 
            const token = JWT.generate({
                _id: result[0]._id,
                username: result[0].username
            }, '1d')
            res.header('Authoization', token)
            res.send({
                ActionType: 'ok',
                data: {
                    username: result[0].username,
                    gender: result[0].gender ? result[0].gender : 0, //性别 0 1 2
                    introduction: result[0].introduction,//简介
                    avatar: result[0].avatar,
                    role: result[0].role,//管理员1 编辑2
                }
            })
        }
    },

全局路由中间件验证token

// 如果token有效,next()

// 如果token过期了,返回401

每次访问刷新token 延续token

app.use((req, res, next) => {
  // 如果token有效,next()
  // 如果token过期了,返回401
  if (req.url == '/adminapi/user/login') {
    next()
    return
  }
  const token = req.headers['authoization'].split(' ')[1]
  if (token) {
    let payload = JWT.verify(token)
    console.log(payload);
    if (payload) {
      const newToken = JWT.generate({
        _id: payload._id,
        username: payload.username
      }, '1d')
      res.header('Authoization', newToken)
      next()
    } else {
      res.status(401).send({ errCode: '-1', errorInfo: 'token过期' })
    }


  }

前端拦截处理token

import axios from 'axios'; // 引入axios
axios.interceptors.request.use(Config => {
    const token = localStorage.getItem('token')
    Config.headers.Authoization = `Beare ${token}`
    return Config
})
axios.interceptors.response.use(function (response) {
    // console.log(response.headers);
    const { authoization } = response.headers
    authoization && localStorage.setItem('token', authoization)
    return response
}, function (error) {
    const { status } = error.response
    if (status == 401) {
        localStorage.removeItem('token')
        window.location.href = '#/login'
        return Promise.reject(error)
    }
})

提示、技巧和注意事项

  • 安全存储密钥: 密钥是 JWT 的关键部分,应安全存储。不要硬编码密钥,最好从环境变量或配置文件中读取。
  • 定期刷新令牌: 为 JWT 设置适当的到期时间,以降低安全风险。客户端应定期获取新的 JWT 令牌。
  • 不要在 JWT 中存储敏感信息 : 避免在 JWT 中存储敏感信息,因为 JWT 可以被解码。如果需要存储敏感信息,应使用加密而不是签名。
相关推荐
QTX187307 分钟前
前端基础的讲解-JS(10)
开发语言·前端·javascript
J总裁的小芒果13 分钟前
el-form el-table 前端排序+校验+行编辑
前端·vue.js·elementui
whyfail20 分钟前
ESM 与 CommonJS:JavaScript 模块化的两大主流方式
javascript·node.js
方方怪26 分钟前
HTML之表单学习记录
前端·学习·html
熊的猫35 分钟前
ES6 中 Map 和 Set
前端·javascript·vue.js·chrome·webpack·node.js·es6
Amd79438 分钟前
Nuxt.js 应用中的 schema:beforeWrite 事件钩子详解
json·vite·配置·nuxt·验证·钩子·动态
布兰妮甜41 分钟前
前端框架大比拼:React.js, Vue.js 及 Angular 的优势与适用场景探讨
前端·vue.js·react.js·前端框架·angular.js
乆夨(jiuze)43 分钟前
vue2.7.14 + vant + vue cli脚手架转vite启动运行问题记录
前端·javascript·vue.js
niech_cn1 小时前
项目启动运行npm run dev报错digital envelope routines::unsupported at new Hash
前端·npm·哈希算法
shchojj1 小时前
cuda的3DArray和TextureObject
前端