使用JSON Web令牌的身份验证和授权简介
#javascript #网络开发人员 #node #jwt

作为一个深入参与Web应用程序的人,我可以强调他们在我们的日常生活中变得多么重要。因此,他们要求可靠的身份验证和授权机制来保护敏感信息免受撬动的眼光。我认为,JSON Web令牌(JWT)提供了一种简单而安全的方法,可以在Node.js应用程序中实现这些机制。在本文中,我将为您提供Node.js中基于JWT的身份验证和授权的详细指南,并提供逐步演练,有用的代码段以及您可以遵循的最佳实践。

什么是JSON Web令牌(JWTS)?

JSON Web令牌(JWTS)是紧凑的URL安全手段,即表示要在两方之间转移的索赔。 JWT由三个部分组成:标头,有效载荷和签名。标头包含有关令牌的元数据,例如用于签名的算法。有效载荷包含索赔,这些索赔是有关实体(通常是用户)和其他数据的语句。该签名用于验证JWT的发件人是否是它是谁,并确保该消息沿途没有变化。

JWT与传统的基于会话的身份验证具有多个优势。首先,他们不需要在服务器上存储用户信息,这使其高度可扩展且易于使用。其次,它们是无状态的,这意味着无需管理服务器端会话。第三,可以在不同的系统和应用程序之间轻松交换它们。

JWT结构的概述

JSON Web令牌(JWTS)是一种由三个部分组成的令牌:标头,有效负载和签名。 JWT以Base64 URL格式编码,这使其易于通过HTTP传输。

Structure of JSON Web Token (jwt)

JWT的标题包含有关令牌的信息,例如用于签名的算法。标题通常是看起来像这样的JSON对象:

{
  "alg": "HS256",
  "typ": "JWT"
}

alg属性指定用于签名的算法,可以是HMAC,RSA或ECDSA。 typ属性指定了令牌的类型,这始终是jwts的jwt。

JWT的有效载荷包含有关用户或客户的索赔或信息。有效载荷也是一个看起来像这样的JSON对象:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

sub属性指定令牌的主题,该主题通常是用户ID。 name属性指定用户的名称。 iat属性指定以Unix时间戳格式发出令牌的时间。

最后,JWT的签名用于验证令牌的完整性。签名是通过将标记的标头和有效载荷与秘密密钥相结合,然后使用标题中指定的算法编码结果来计算的。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
) secret base64 encoded

JWT声称及其意义

JWT声称是令牌有效载荷中包含的有关用户或客户端的信息。索赔可用于提供有关用户或客户端的上下文,例如其ID或角色,并且可用于身份验证和授权目的。

JWT中有三种类型的主张:

  1. 注册索赔 - 这些是JWT中常用的一组预定义索赔。注册索赔的一些示例包括ISS(发行人),EXP(到期时间)和NBF(不是之前)。注册的索赔是可选的,但是使用它们可以使您的JWT与其他系统更加互操作。
  2. 公开主张 - 这些是用户定义的主张,旨在与他人共享。公众索赔应遵循JWT规范中指定的命名约定。
  3. 私人主张 - 这些是特定于您的申请的说法,并且不打算与他人共享。私人索赔应使用特定应用程序的命名空间,以避免与其他索赔发生冲突。

JWT索赔的使用

JWT索赔可用于您的应用程序中的各种目的,包括:

  1. 身份验证 - JWT可以通过在确定用户的有效载荷中包含sub索赔来对用户进行身份验证。当用户登录时,您可以使用用户ID作为SUB Simep创建JWT,然后使用JWT在后续请求中对用户进行身份验证。
  2. 授权 - 可用于通过在指定用户角色或权限的有效载荷中包含角色主张来授权用户。当用户提出请求时,您可以验证其JWT并检查角色索赔是否包含所请求的操作所需的角色。
  3. 无状态性,由于JWT包含有关用户或客户端的所有必要信息,因此可以使用它们来创建无状态的API。这意味着您不需要将会话数据存储在服务器上,这可以提高可伸缩性并降低复杂性。

JWT的好处

  1. 无状态:JWT是无状态的,这意味着服务器无需跟踪会话或令牌。这使JWTS更易于扩展,并且更有效地用于分布式系统中。
  2. 跨域:JWT可以在不同的域和服务器上使用,使其成为现代Web应用程序中身份验证和授权的灵活解决方案。
  3. 安全性:JWT是使用秘密键以数字方式签名的,这使攻击者很难与令牌伪造或篡改。此外,可以对JWT进行加密以提供额外的安全层。
  4. 分散的:JWT可以通过不同的服务生成和验证,允许在微服务体系结构中进行分散的身份验证和授权。
  5. 有效载荷:JWTS可以包含用户数据或用户权限等用户数据的有效载荷,从而使其可用于授权和身份验证。

在Node.js中使用JWT身份验证

身份验证是验证试图访问资源或服务的用户或客户端的身份的过程。在node.js中,通常通过要求用户提供一组有效的凭据(例如用户名和密码)来访问该应用程序的有效凭据来实现身份验证。然后,服务器对数据库或第三方身份验证服务检查提供的凭据,如果凭据有效,则将授予用户访问。

用户或客户端将登录到Node.js Web应用程序时,服务器会生成JWT并将其发送回客户端。该JWT包含一组索赔,这些索赔是有关用户或客户端的信息,例如其ID或角色。然后将JWT存储在客户端,通常在cookie或本地存储中。

使用JWT的身份验证过程涉及三个步骤:(1)客户端以其凭据发送登录请求,(2)服务器验证凭据并生成JWT,(3)服务器将JWT发送给客户端,然后可以使用它来访问受保护的资源。

要在node.js中实现基于JWT的身份验证,您可以使用jsonwebtoken图库。这是一个分步指南:

  1. 使用npm安装jsonwebtoken图库。
`npm install jsonwebtoken`
  1. 在您的node.js应用程序中创建一个接受用户凭据的登录路由(例如,用户名和密码)。
  2. 验证凭据并使用jsonwebtoken图书馆生成JWT。
  3. 将JWT发送回客户端以作为响应。
app.post('/login', (req, res) => {
  const { username, password } = req.body;

  // Check if the user exists and the password is correct
  const user = users.find(user => user.username === username && user.password === password);

  if (!user) {
    return res.status(401).json({ message: 'Invalid username or password' });
  }

  // Generate a JWT with a secret key
  const payload = { username: user.username };
  const secretKey = 'mySecretKey';
  const options = { expiresIn: '1h' };
  const token = jwt.sign(payload, secretKey, options);

  // Return the token to the client
  res.status(200).json({ token });
});

在上面的示例中,我们创建了一个login路由,该路由接受客户端的用户名和密码。然后,我们检查用户是否存在并且密码正确。如果凭据有效,我们使用jsonwebtoken图库生成JWT,并将其作为响应发送给客户端。

在Node.js中使用JWT授权

另一方面,

授权是确定用户或客户端是否具有访问特定资源或在应用程序中执行特定操作的必要权限的过程。在Node.js中,通常通过根据其身份或其他属性为用户分配角色或权限来实现授权。例如,可以授权具有管理角色的用户访问和修改应用程序的某些部分,而常规用户只能被授权查看某些资源。

在JWT的上下文中的授权通常是通过将用户或客户权限包括在JWT中的主张来实现的。例如,具有管理角色的用户可能会在其JWT中具有管理要求,这使他们可以访问常规用户无法访问的应用程序的某些部分。然后,服务器可以检查JWT中的管理要求的存在,以确定用户是否有权执行特定的操作或访问特定资源。

客户端具有有效的JWT后,他们可以使用它来访问服务器上受保护的资源。要在Node.js中实现基于JWT的授权,您可以使用中间件功能来验证JWT并在允许访问受保护资源之前检查用户的权限。

这是用于在节点中实现基于JWT的授权的分步指南:

  1. 创建一个中间件功能,该功能验证JWT并在允许访问受保护资源之前检查用户的权限。
const authMiddleware = (req, res, next) => {
  const token = req.headers.authorization;

  if (!token) {
    return res.status(401).json({ message: 'No token provided' });
  }

  jwt.verify(token, secretKey, (err, decoded) => {
    if (err) {
      return res.status(401).json({ message: 'Invalid token' });
    }

    req.user = decoded;
    next();
  });
};

app.get('/protected', authMiddleware, (req, res) => {
  res.status(200).json({ message: 'You are authorized to access this resource' });
});

在上面的示例中,我们创建了一个中间件功能,该功能验证JWT并将解码的有效载荷设置为req.user对象。然后,我们使用此中间件功能来保护/protected路线。

当客户端试图访问/protected路由时,中间件功能将在授权标题中验证JWT。如果JWT有效,则中间件功能将设置req.user对象,并允许客户端访问受保护的资源。如果JWT无效或丢失,中间件功能将返回错误响应。

令牌刷新和到期

JWT的关键特征之一是它们能够为令牌设置到期时间。通过确保不能无限期地使用令牌,这提供了附加的安全层。令牌到期后,用户必须获得新的令牌才能继续访问受保护的资源。

除了设置过期时间外,JWT还可以包括刷新令牌。刷新令牌是一个单独的令牌,用于在原始令牌过期后用于获得新的JWT。这可以通过允许用户继续访问受保护的资源而不必不断登录来提供更好的用户体验。

要在您的node.js应用程序中实现令牌刷新和到期,您可以使用中间件和令牌管理库的组合。这里是如何使用jsonwebtoken库实现令牌刷新和到期的一个示例:

const jwt = require('jsonwebtoken');
const refreshTokenSecret = 'your_refresh_token_secret';
const accessTokenSecret = 'your_access_token_secret';
const refreshTokens = [];

// Middleware to check if user is authenticated
const authenticateJWT = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (authHeader) {
    const token = authHeader.split(' ')[1];

    jwt.verify(token, accessTokenSecret, (err, user) => {
      if (err) {
        return res.sendStatus(403);
      }

      req.user = user;
      next();
    });
  } else {
    res.sendStatus(401);
  }
};

// Endpoint to generate new access and refresh tokens
app.post('/token', (req, res) => {
  const { refreshToken } = req.body;
  if (!refreshToken || !refreshTokens.includes(refreshToken)) {
    return res.sendStatus(403);
  }

  jwt.verify(refreshToken, refreshTokenSecret, (err, user) => {
    if (err) {
      return res.sendStatus(403);
    }

    const accessToken = jwt.sign({ username: user.username }, accessTokenSecret, { expiresIn: '15m' });
    res.json({ accessToken });
  });
});

// Endpoint to log in and generate access and refresh tokens
app.post('/login', (req, res) => {
  // authenticate user
  const username = req.body.username;
  const user = { username: username };

  const accessToken = jwt.sign(user, accessTokenSecret, { expiresIn: '15m' });
  const refreshToken = jwt.sign(user, refreshTokenSecret);
  refreshTokens.push(refreshToken);

  res.json({ accessToken, refreshToken });
});

// Endpoint to log out and revoke refresh token
app.post('/logout', (req, res) => {
  const { refreshToken } = req.body;
  if (!refreshToken) {
    return res.sendStatus(400);
  }

  const index = refreshTokens.indexOf(refreshToken);
  if (index !== -1) {
    refreshTokens.splice(index, 1);
  }

  res.sendStatus(204);
});

在此示例中,我们使用两个JWT秘密:一个用于访问令牌,一个用于刷新令牌。我们还将刷新令牌存储在服务器上的数组中,以便我们可以检查给定的刷新令牌是否有效。

authenticateJWT中间件检查是否通过验证访问令牌来验证用户。如果令牌有效,则将req.user属性设置为用户对象。

/token端点用于使用有效的刷新令牌生成新的访问令牌。如果刷新令牌有效,我们将在15分钟到期时间内对其进行验证并生成新的访问令牌。

/login端点用于登录并生成新的访问令牌和刷新令牌对。我们同时生成令牌并将刷新令牌存储在刷新阵列中

基于JWT的身份验证和授权的最佳实践

在Node.js中实施基于JWT的身份验证和授权时,这里有一些最佳实践:

  1. 始终使用安全的随机秘密密钥来签名和验证JWT。
  2. 使用短暂的JWT(例如一小时)来最大程度地减少令牌劫持的风险。
  3. 实施利率限制和其他安全措施,以防止对登录端点的蛮力攻击。
  4. 使用https加密客户端和服务器之间的所有通信。
  5. 存储敏感用户数据(例如,密码)使用哈希和腌制。
  6. 使用与JWT相关的标题和有效载荷字段使用一致的命名约定。
  7. 使用中间件功能来验证JWT和保护资源。

结论

JSON Web令牌(JWTS)提供了一种安全有效的方法,可以在Node.js应用程序中实现身份验证和授权。通过遵循本文中概述的最佳实践,您可以确保基于JWT的身份验证和授权系统稳健且安全。