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

目录

JWT 使用场景

JWT 基本概念

常用方法

1. 生成 JWT

2. 验证 JWT

3. 中间件

实践案例

项目实战

封装jwt文件

后端登录接口生成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 可以被解码。如果需要存储敏感信息,应使用加密而不是签名。
     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值