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 可以被解码。如果需要存储敏感信息,应使用加密而不是签名。
相关推荐
一條狗2 分钟前
隨筆 20241224 ts寫入excel表
开发语言·前端·typescript
chenchihwen6 分钟前
数据分析时的json to excel 转换的好用小工具
数据分析·json·excel
小码快撩7 分钟前
vue应用移动端访问缓慢问题
前端·javascript·vue.js
低调之人11 分钟前
Fiddler勾选https后google浏览器网页访问不可用
前端·测试工具·https·fiddler·hsts
Riesenzahn17 分钟前
使用vue如何监听元素尺寸的变化?
前端·javascript
阿征学IT21 分钟前
圣诞快乐(h5 css js(圣诞树))
前端·javascript·css
程序员黄同学24 分钟前
如何使用 Flask 框架创建简单的 Web 应用?
前端·python·flask
Sword9925 分钟前
豆包 MarsCode AI Apply功能揭秘:自动代码应用与 Diff 实现
前端·人工智能·豆包marscode
前端与小赵25 分钟前
什么是全栈应用,有哪些特点
前端
a1ex25 分钟前
shadcn/ui 动态 pagination
前端