• JWT开发详解


    一、JWT理论基础

    JSON Web Token(JWT)是一种开放标准(RFC 7519),用于在网络上以紧凑且自包含的方式安全地传输信息。JWT 被设计为在用户和服务之间传递声明信息,以便进行身份验证和授权。该标准定义了一种简洁的、自包含的方法,可以传递使用 JSON 对象进行编码的信息,这些信息可以被验证和信任。

    JWT 由三个部分组成:Header、Payload 和 Signature。这三部分通过.符号分隔,并以Base64编码的形式进行表示。

    1. Header(头部):

      • Header 包含了两部分信息,分别是 token 的类型(JWT)和使用的签名算法,例如:
        {
          "alg": "HS256",
          "typ": "JWT"
        }
        
        • 1
        • 2
        • 3
        • 4
      • 上述例子表示使用 HMAC SHA-256 算法进行签名。
    2. Payload(负载):

      • Payload 包含了声明(claims)。声明是关于实体(通常是用户)和其他数据的声明。有三种类型的声明:

        • 注册声明(Registered claims): 这些是一组预定义的声明,包括 iss(签发者)、sub(主题)、aud(受众)、exp(过期时间)、nbf(不可用之前的时间)和 iat(发布时间)。
        • 公共声明(Public claims): 可以根据需要定义的声明。
        • 私有声明(Private claims): 用于在同意使用它们的双方之间共享信息。
      • 例如:

        {
          "sub": "1234567890",
          "name": "John Doe",
          "admin": true
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
    3. Signature(签名):

      • 使用编码的 header、编码的 payload 和一个秘密密钥来创建签名部分。签名用于验证消息在传递过程中是否被篡改。

      • 例如:

        HMACSHA256(
          base64UrlEncode(header) + "." +
          base64UrlEncode(payload),
          secret)
        
        • 1
        • 2
        • 3
        • 4

    JWT 的工作流程通常如下:

    1. 用户进行身份验证,并且身份验证成功后,服务器会生成一个包含用户信息的 JWT,并将其返回给用户。
    2. 用户在之后的请求中将 JWT 放入请求的头部(通常是 Authorization 头部)中。
    3. 服务器在接收到请求时,使用之前共享的密钥来验证 JWT 的签名,以确保信息的完整性。
    4. 如果签名验证成功,服务器可以使用 JWT 中的信息进行授权判断。

    优势和特点:

    • 简洁性: JWT 使用 JSON 数据格式,具有良好的可读性和编码/解码的便捷性。
    • 自包含: JWT 包含了所有需要的信息,避免了每次请求时都需要查询数据库的开销。
    • 可靠性: JWT 的签名机制保证了消息的完整性,防止了信息被篡改的可能性。
    • 跨域: 可以在不同的域之间进行信息传递,支持跨域应用场景。

    需要注意的是,由于 JWT 使用了 Base64 编码,虽然可以在客户端解码查看,但不能修改,因为修改后签名验证将失败。然而,JWT 中的信息仍然是可见的,因此敏感信息应该被妥善处理。

    二、JWT使用实例

    2.1 服务端代码

    编写一个完整的 JWT 项目涉及多个方面,包括用户认证、生成和验证 JWT、路由保护等。下面是一个简单的示例,使用 Node.js 和 Express 框架实现:

    首先,确保你已经安装了 Node.js 和 npm。然后,创建一个新目录,执行以下步骤:

    1. 初始化项目:

      npm init -y
      
      • 1
    2. 安装依赖:

      npm install express jsonwebtoken body-parser
      
      • 1
    3. 创建一个 server.js 文件:

      const express = require('express');
      const jwt = require('jsonwebtoken');
      const bodyParser = require('body-parser');
      
      const app = express();
      const PORT = 3000;
      const SECRET_KEY = 'your_secret_key';
      
      app.use(bodyParser.json());
      
      // 用户数据库(示例)
      const users = [
        { id: 1, username: 'user1', password: 'password1' },
        { id: 2, username: 'user2', password: 'password2' },
      ];
      
      // 登录路由
      app.post('/login', (req, res) => {
        const { username, password } = req.body;
      
        // 实际项目中应该通过数据库查询验证用户信息
        const user = users.find((u) => u.username === username && u.password === password);
      
        if (user) {
          // 生成 JWT
          const token = jwt.sign({ userId: user.id, username: user.username }, SECRET_KEY, { expiresIn: '1h' });
          res.json({ token });
        } else {
          res.status(401).json({ error: 'Invalid credentials' });
        }
      });
      
      // 保护路由
      app.get('/protected', (req, res) => {
        const token = req.header('Authorization');
      
        if (!token) {
          return res.status(401).json({ error: 'Unauthorized' });
        }
      
        // 验证 JWT
        jwt.verify(token, SECRET_KEY, (err, decoded) => {
          if (err) {
            return res.status(401).json({ error: 'Invalid token' });
          }
      
          res.json({ message: 'You are authorized!', user: decoded });
        });
      });
      
      app.listen(PORT, () => {
        console.log(`Server is running on http://localhost:${PORT}`);
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
    4. 运行应用:

      node server.js
      
      • 1

    现在,你的应用将在 http://localhost:3000 上运行。你可以使用 Postman 或其他工具模拟登录请求,然后将生成的 token 用于访问 /protected 路由。

    请注意,以上示例是一个简单的演示,实际项目中需要更复杂的用户认证和安全性措施。在生产环境中,你可能需要使用 HTTPS、存储更安全的密钥、使用数据库进行用户验证等。

    2.2 客户端代码

    客户端部分通常是一个前端应用,可以使用 HTML、CSS、JavaScript(通常使用框架如 React、Vue.js 或 Angular)来实现。在客户端,主要任务包括用户界面的设计、用户登录、JWT 的处理、以及向服务器发起请求等。

    以下是一个简单的 HTML、CSS、JavaScript 示例,使用原生 JavaScript 实现:

    1. 创建一个 index.html 文件:
    DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>JWT Demo - Clienttitle>
    head>
    <body>
      <h1>JWT Demo - Clienth1>
      <div id="login-form">
        <label for="username">Username:label>
        <input type="text" id="username" required>
        <br>
        <label for="password">Password:label>
        <input type="password" id="password" required>
        <br>
        <button onclick="login()">Loginbutton>
      div>
      <div id="protected-content" style="display: none;">
        <h2>Protected Contenth2>
        <p id="message">p>
      div>
    
      <script>
        function login() {
          const username = document.getElementById('username').value;
          const password = document.getElementById('password').value;
    
          fetch('http://localhost:3000/login', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({ username, password }),
          })
          .then(response => response.json())
          .then(data => {
            if (data.token) {
              // 登录成功,显示保护内容
              document.getElementById('login-form').style.display = 'none';
              document.getElementById('protected-content').style.display = 'block';
              document.getElementById('message').innerText = 'Loading...';
    
              // 使用获取的 token 访问受保护的路由
              fetch('http://localhost:3000/protected', {
                headers: {
                  'Authorization': data.token,
                },
              })
              .then(response => response.json())
              .then(protectedData => {
                document.getElementById('message').innerText = `Welcome, ${protectedData.user.username}!`;
              })
              .catch(error => console.error('Error fetching protected content:', error));
            } else {
              alert('Invalid credentials');
            }
          })
          .catch(error => console.error('Login error:', error));
        }
      script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    这个简单的 HTML 文件包含一个登录表单和一个显示受保护内容的部分。当用户成功登录后,它会获取 JWT,并使用该令牌向服务器发起受保护路由的请求。

    请注意,这只是一个基础示例,实际项目中应该考虑更多的安全性和用户体验问题。前端应用通常会使用一些框架,以更好地组织和管理代码。

    三、JWT流程

    以下是一个使用 Mermaid 语法绘制的简单 JWT 流程图:

    1. 提供用户名和密码
    2. 验证用户信息
    3. 发送JWT
    4. 包含JWT的请求
    5. 验证JWT
    6. 返回受保护资源
    用户
    服务器
    生成JWT
    服务器
    授权访问

    这个流程图简要描述了 JWT 的使用过程:

    1. 用户提供用户名和密码。
    2. 服务器验证用户信息。
    3. 如果验证成功,服务器生成 JWT。
    4. 服务器将包含 JWT 的响应发送给用户。
    5. 用户在后续请求中包含 JWT。
    6. 服务器验证 JWT 并授权用户访问受保护资源。

    请注意,这只是一个高层次的概述,实际流程可能因应用场景而异,例如,在生成 JWT 时可能涉及更多的步骤,而在验证 JWT 时可能涉及更多的安全性检查。

  • 相关阅读:
    基于Python通信程序的设计与实现
    Linux多线程编程实例解析
    html关闭空标签
    SpringMVC全局异常处理+拦截器使用+参数校验
    Python运维监控系统之架构设计
    校验系统文件名是否符合标准
    【Java】Map集合
    Python图像处理丨认识图像锐化和边缘提取的4个算子
    DPDK的VFIO
    <C++>文件操作基础详解,快来写出你的第一个文件吧
  • 原文地址:https://blog.csdn.net/ARV000/article/details/134392661