在node中使用JWT认证

在node中使用JWT认证

JWTJSON Web Tokens) 是一种流行的身份验证方法,可让我们以 JSON 对象的形式在各​​方之间安全地传输信息。在本文中,我们将介绍如何在 Node.js 应用程序中 使用JWT 身份验证。

什么是JWT

JWT 是一个紧凑且独立的 JSON 对象,其中包含有关用户身份验证的信息。该信息使用密钥进行数字签名,可以被验证和信任。

JWT 由以下几方面组成:

  • header(标头):包含有关 JWT 编码方式的信息,例如用于签署令牌的算法。
  • payload(有效负载):这包含声明。声明是关于实体(通常是用户)和附加元数据的声明。声明分为三种类型:注册声明、公共声明和私人声明。
  • signature(签名):这用于验证 JWT 的发送者是否是其声称的身份,并确保消息在此发送过程中没有被更改。签名是通过获取编码标头、编码有效负载和密钥,然后使用指定算法对它们进行创建的。

以下是一个 JWT 示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

设置环境

在我们讲解之前,让我们设置我们的开发环境。我们将使用以下工具和包:

  • Node.js:我们将使用 Node.js 作为服务器端语言。
  • Express:这是一个流行的 Node.js Web 框架,我们将用它来处理 HTTP 请求。
  • jsonwebtoken:这是一个流行的库,用于在 Node.js 中生成和验证 JWT

让我们首先使用以下命令创建一个新的 Node.js 项目:

npm init

接下来,运行以下命令来安装所需的软件包:

npm install express jsonwebtoken

实现JWT身份验证

首先,让我们创建一个名为index.js 的新文件,并添加以下代码来设置我们的 Express 应用程序:

const express = require('express');
const app = express();
const port = 3000;

app.listen(port, () => {
    
    
  console.log(`Server running at http://localhost:${
      
      port}`);
});

接下来,让我们创建一个简单的路由,如果用户通过身份验证,它将返回一条消息:

app.get('/protected', (req, res) => {
    
    
  res.send('Welcome to the protected route');
});

通过添加中间件来实现身份验证过程,该中间件将检查传入请求标头中是否存在有效的 JWT

const jwt = require('jsonwebtoken');
const secretKey = 'secret_key';

const authenticate = (req, res, next) => {
    
    
  const token = req.headers['authorization'];
  if (!token) {
    
    
    return res.status(401).send('Access Denied. No token provided.');
  }

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

app.get('/protected', authenticate, (req, res) => {
    
    
  res.send('Welcome to the protected route');
});

在上面的代码中,我们导入了jsonwebtoken库并创建了用于签名和验证 JWT 的密钥。然后,我们创建了一个名为 authenticate 的中间件函数,该函数将用于检查传入请求标头Authorization中的有效 JWT

如果存在令牌,我们将使用jsonwebtoken库中的verify方法来解码令牌并检查其有效性。如果令牌有效,我们将解码后的信息添加到req对象中,并调用next函数以继续下一个中间件或路由处理程序。

如果未提供令牌或令牌无效,我们将分别返回带有401400状态码的错误消息。

生成JWT

现在我们已经完成了身份验证过程,让我们创建一个用于生成 JWT 的路由。例如,这可以在用户登录时完成。

app.post('/login', (req, res) => {
    
    
  const user = {
    
    
    id: 1,
    username: 'leo'
  };
  const token = jwt.sign({
    
     user }, secretKey, {
    
     expiresIn: '1h' });
  res.header('Authorization', token).send(user);
});

在上面的代码中,我们创建了一个/login路由,当收到POST请求时,该路由将生成 JWT。我们创建了一个模拟用户对象,并使用jsonwebtoken库中的sign方法生成 JWT。我们指定了密钥并将expiresIn选项设置为1h,这意味着令牌将在一小时后过期。

最后,我们将生成的 JWT 添加到响应标头的Authorization字段中,并将用户信息发送回客户端。

实现令牌刷新

JWT 的一个问题是它们的寿命很短,需要定期刷新。为了解决这个问题,我们可以使用刷新令牌。

刷新令牌是与访问令牌是不同的,它们可用于生成新的访问令牌。出于安全原因,这使我们能够保持访问令牌的短暂性,同时仍然允许用户在更长的时间内保持身份验证。

我们修改/login路由以生成访问令牌和刷新令牌:

app.post('/login', (req, res) => {
    
    
	const user = {
    
    
		id: 1,
		username: 'leo'
	};
	const accessToken = jwt.sign({
    
     user }, secretKey, {
    
     expiresIn: '1h' });
	const refreshToken = jwt.sign({
    
     user }, secretKey, {
    
     expiresIn: '1d' });
	res.cookie('refreshToken', refreshToken, {
    
     httpOnly: true, sameSite: 'strict' })
		.header('Authorization', accessToken)
		.send(user);
});

接下来,我们将创建一条用于刷新访问令牌的新路由。该路由将从客户端接收刷新令牌并使用它生成新的访问令牌:

app.post('/refresh', (req, res) => {
    
    
  const refreshToken = req.cookies['refreshToken'];
  if (!refreshToken) {
    
    
    return res.status(401).send('Access Denied. No refresh token provided.');
  }

  try {
    
    
    const decoded = jwt.verify(refreshToken, secretKey);
    const accessToken = jwt.sign({
    
     user: decoded.user }, secretKey, {
    
     expiresIn: '1h' });

    res
      .header('Authorization', accessToken)
      .send(decoded.user);
  } catch (error) {
    
    
    return res.status(400).send('Invalid refresh token.');
  }
});

在上面的代码中,我们创建了一个用于刷新访问令牌的新路由。该路由检查 cookie 中是否存在刷新令牌,如果找到令牌,我们将使用verify方法检查其有效性。如果刷新令牌有效,我们将生成一个与原始令牌具有相同信息的新访问令牌,并将其发送回客户端。

最后,我们可以更新authenticate中间件以检查访问令牌和刷新令牌,并在必要时刷新访问令牌:

const authenticate = (req, res, next) => {
    
    
  const accessToken = req.headers['authorization'];
  const refreshToken = req.cookies['refreshToken'];

  if (!accessToken && !refreshToken) {
    
    
    return res.status(401).send('Access Denied. No token provided.');
  }

  try {
    
    
    const decoded = jwt.verify(accessToken, secretKey);
    req.user = decoded.user;
    next();
  } catch (error) {
    
    
    if (!refreshToken) {
    
    
      return res.status(401).send('Access Denied. No refresh token provided.');
    }

    try {
    
    
      const decoded = jwt.verify(refreshToken, secretKey);
      const accessToken = jwt.sign({
    
     user: decoded.user }, secretKey, {
    
     expiresIn: '1h' });

      res
        .cookie('refreshToken', refreshToken, {
    
     httpOnly: true, sameSite: 'strict' })
        .header('Authorization', accessToken)
        .send(decoded.user);
    } catch (error) {
    
    
      return res.status(400).send('Invalid Token.');
    }
  }
};

在上面的代码中,我们首先检查访问令牌和刷新令牌是否存在。如果仅提供了访问令牌,我们会检查其有效性,如果有效则继续请求。如果访问令牌无效,我们将检查是否存在刷新令牌。如果找到刷新令牌,我们将验证其有效性并根据刷新令牌中的信息生成新的访问令牌。如果刷新令牌无效,我们会向客户端发送一条错误消息。

完成这些更改后,我们现在拥有一个带有刷新令牌机制的完整 JWT 身份验证系统。要使用该系统,客户端可以首先向路由发出请求/login以接收访问令牌和刷新令牌。然后,他们可以将访问令牌包含在所有后续请求标头中的Authorization以访问受保护的路由。如果访问令牌过期,客户端可以向路由发出请求/refresh以接收新的访问令牌。

猜你喜欢

转载自blog.csdn.net/qq_42880714/article/details/132959711