我们一起聊一聊JWT的那些事

我们一起聊一聊JWT的那些事

一切美好,如期而至......

什么是JWT

JWT,全称为 JSON Web Token,是一种用于在网络上安全地传递信息的开放标准(RFC 7519)。JWT 是一种紧凑且独立的方式,用于在各方之间以 JSON 对象形式安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用秘密(使用 HMAC 算法)或使用公钥/私钥对(使用 RSA 或 ECDSA 算法)进行签名。

JWT 通常用于身份验证和信息交换。在身份验证方面,当用户成功登录后,服务器会生成一个包含用户标识信息的 JWT,并将其发送回客户端。客户端随后可以在每个后续请求中包含 JWT,以证明其身份。在信息交换方面,由于 JWT 的信息是经过签名的,接收方可以验证其来源和完整性。

JWT 的结构如下:

复制代码
xxxxx.yyyyy.zzzzz

其中,每一部分由点号分隔:

  • 第一部分是头部(Header),通常包含两部分信息:令牌类型(JWT)和所使用的签名算法,例如 HMAC SHA256 或 RSA。

  • 第二部分是载荷(Payload),包含声明(claims)。声明是关于实体(通常是用户)和其他数据的声明。有三种类型的声明:注册的声明,公共的声明和私有的声明。

  • 第三部分是签名(Signature),用于验证消息在传递过程中没有被篡改,并确保消息的发送方是可信的。

JWT 的优点包括:

  1. 自包含: JWT 包含了所有需要的信息,减少了在服务器存储会话状态的需要。

  2. 跨域: 由于 JWT 是在各方之间传递的标准格式,可以在不同域之间轻松传递信息。

  3. 安全性: 当使用数字签名时,可以确保 JWT 的来源和完整性。

然而,需要注意的是,为了确保安全性,不应该在 JWT 中存储敏感信息,尤其是在使用对称加密算法的情况下。对于敏感信息,最好在服务器端进行验证。

和普通的token有啥区别子

JSON Web Token(JWT)和普通的 token 之间有一些关键区别,主要涉及到它们的结构、用途和安全性等方面:

  1. 结构差异:

    • 普通 Token: 通常是一个简单的字符串,可能是随机生成的一串字符,例如 UUID(Universally Unique Identifier)。
    • JWT: 具有明确定义的结构,包含头部、载荷和签名三个部分,并以点号分隔。这种结构使得 JWT 可以携带更多的信息,并且具有更灵活的用途。
  2. 内容携带:

    • 普通 Token: 只是一个标识符,不携带其他有关用户或实体的信息。
    • JWT: 可以携带一些声明(claims),这些声明包含有关用户、角色、权限等信息。由于 JWT 是基于 JSON 格式的,因此它可以以一种结构化的方式携带更多的信息。
  3. 签名和验证:

    • 普通 Token: 通常需要在服务器端进行验证,但不一定使用数字签名或加密。
    • JWT: 通常使用数字签名来验证其真实性。这使得 JWT 在传递过程中能够被验证,确保它没有被篡改。JWT 还支持使用非对称加密来提供额外的安全性。
  4. 安全性:

    • 普通 Token: 可能存在被伪造或截获的风险,因为它没有内置的安全机制。
    • JWT: 通过数字签名或加密,提供了一定程度的安全性。如果使用对称加密,需要确保密钥的安全性;如果使用非对称加密,可以更安全地传递公钥。
  5. 用途:

    • 普通 Token: 通常用于简单的身份验证,例如在会话中保存用户的登录状态。
    • JWT: 由于其结构和灵活性,可用于更广泛的用途,包括身份验证、信息交换和声明传递。

总体而言,JWT 是一种更为灵活、结构化且安全的令牌,适用于需要携带更多信息和在跨域环境中进行安全传递的场景。普通 Token 更适合简单的身份验证场景。选择使用哪种令牌取决于具体的应用需求和安全要求。

如何生成JWT

前端生成

生成 JSON Web Tokens(JWT)涉及以下步骤:

  1. 选择一个密钥: JWT 使用密钥进行签名以确保数据的完整性和来源。密钥可以是对称密钥(HMAC算法)或非对称密钥(RSA或ECDSA算法)。

  2. 构建JWT的头部(Header): JWT的头部包含有关令牌的元信息,如算法和令牌类型。这部分信息通常以Base64编码的JSON字符串表示,并放置在JWT的第一部分。

  3. 构建JWT的载荷(Payload): 载荷包含有关JWT主体(subject)的声明和其他信息。与头部一样,这部分信息也是以Base64编码的JSON字符串表示,并放置在JWT的第二部分。

  4. 对头部和载荷进行签名: 使用选择的算法和密钥对头部和载荷进行签名。签名是通过将Base64编码的头部和载荷字符串与密钥一起进行加密而生成的。

  5. 将头部、载荷和签名组合成JWT: 将Base64编码的头部、Base64编码的载荷和签名以点号分隔组合在一起形成JWT。

下面是一个简单的例子,使用Node.js中的jsonwebtoken库生成JWT:

javascript 复制代码
const jwt = require('jsonwebtoken');

// 构建头部
const header = {
  "alg": "HS256",  // HMAC SHA-256算法
  "typ": "JWT"
};

// 构建载荷
const payload = {
  "sub": "1234567890",  // 主体标识
  "name": "John Doe",
  "iat": Math.floor(Date.now() / 1000)  // 签发时间(当前时间)
};

// 选择密钥
const secretKey = "yourSecretKey";

// 生成JWT
const token = jwt.sign(payload, secretKey, { header });

console.log(token);

在实际应用中,建议使用更安全的随机生成的密钥,并根据具体的需求选择适当的算法和配置选项。生成JWT的代码示例可能会根据所选的编程语言和库而有所不同,但上述步骤是通用的。

后端生成

在Java中,你可以使用 io.jsonwebtoken 库来生成 JSON Web Tokens(JWT)。以下是一个简单的例子:

首先,你需要将 jjwt 库添加到你的项目中。如果使用 Maven,可以在 pom.xml 文件中添加以下依赖:

xml 复制代码
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>

然后,你可以使用以下Java代码生成JWT:

java 复制代码
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

public class JwtGenerator {

    public static void main(String[] args) {
        // 构建头部
        String algorithm = SignatureAlgorithm.HS256.getJcaName();
        String header = "{\"alg\":\"" + algorithm + "\",\"typ\":\"JWT\"}";

        // 构建载荷
        String subject = "1234567890";  // 主体标识
        String name = "John Doe";
        long nowMillis = System.currentTimeMillis();
        Date iat = new Date(nowMillis);  // 签发时间(当前时间)

        // 选择密钥
        String secretKey = "yourSecretKey";

        // 生成JWT
        String token = Jwts.builder()
                .setHeader(header)
                .setSubject(subject)
                .claim("name", name)
                .setIssuedAt(iat)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();

        System.out.println(token);
    }
}

请确保替换示例中的密钥和其他参数为你实际的值。这个例子中使用了 HMAC SHA-256 算法,但你可以根据需求选择其他算法。在实际应用中,密钥应该是安全的,并且可能需要使用环境变量或其他方式来管理。

还有那些库

除了 io.jsonwebtoken,还有一些其他常用的Java库可以用于生成和处理JSON Web Tokens(JWT)。以下是一些常见的JWT库:

  1. Nimbus JOSE + JWT:

    • 官方网站: Nimbus JOSE + JWT
    • 主要功能:
      • 提供了处理JOSE(JSON Object Signing and Encryption)和JWT的Java库。
      • 支持各种JWT相关的标准和规范,如JWT、JWS(JSON Web Signature)、JWE(JSON Web Encryption)等。
      • 提供了易于使用的API,支持生成和验证JWT。
  2. Java-JWT:

    • GitHub地址: Java-JWT
    • 主要功能:
      • Auth0开发的Java库,用于生成和验证JWT。
      • 支持标准的JWT算法,如HMAC SHA256、RS256等。
      • 提供了简洁的API,适用于各种JWT用例。
  3. JJWT:

    • GitHub地址: JJWT
    • 主要功能:
      • 一个简单的Java库,用于生成、解析和验证JWT。
      • 支持各种算法,包括HMAC SHA256、RS256等。
      • 提供了流畅的API,易于使用。 当然,还有HuTool工具类,就不多赘述了

选择使用哪个库取决于你的具体需求、项目的特点以及个人偏好。在使用之前,请仔细查阅相关文档,了解库的功能、性能和安全性。

JWT的解析和验证

解析和验证 JSON Web Tokens(JWT)通常包括以下步骤:

  1. 拆分 JWT: JWT 由三部分组成,分别是头部(Header)、载荷(Payload)、签名(Signature),它们之间用点号分隔。首先,将 JWT 字符串拆分成这三个部分。

  2. 解码 Base64: 对头部和载荷进行 Base64 解码,得到 JSON 字符串。

  3. 解析 JSON: 将解码后的头部和载荷 JSON 字符串解析为 JSON 对象。

  4. 验证签名(可选): 如果 JWT 使用签名算法进行签名,需要对头部、载荷和签名进行验证。验证的步骤通常包括获取密钥(对称或非对称密钥),使用相同的签名算法对头部和载荷进行签名,然后比较生成的签名与 JWT 中的签名是否匹配。

以下是一个使用 Java 的示例,使用 io.jsonwebtoken 库进行 JWT 解析和验证的过程:

java 复制代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;

public class JwtParser {

    public static void main(String[] args) {
        // 要解析和验证的 JWT 字符串
        String jwtString = "yourJwtStringHere";

        try {
            // 解析 JWT
            Jws<Claims> jws = Jwts.parserBuilder()
                    .setSigningKey("yourSecretKey")  // 设置密钥
                    .build()
                    .parseClaimsJws(jwtString);

            // 获取载荷(Claims)
            Claims claims = jws.getBody();

            // 在此可以获取 JWT 中的信息,例如:
            String subject = claims.getSubject();
            String name = (String) claims.get("name");
            // 其他声明的获取方法...

            // 验证通过,可以继续处理业务逻辑
            System.out.println("JWT验证通过");

        } catch (ExpiredJwtException e) {
            // JWT 过期异常
            System.out.println("JWT已过期");
        } catch (SignatureException e) {
            // JWT 签名异常
            System.out.println("JWT签名验证失败");
        } catch (Exception e) {
            // 其他异常
            System.out.println("JWT解析失败");
        }
    }
}

请注意,上述代码中的密钥和其他参数需要根据你的实际情况进行调整。此外,对于非对称加密的情况,需要提供公钥进行验证。在实际应用中,也可以考虑使用库提供的更复杂的配置选项和错误处理机制。

相关推荐
小江的记录本17 分钟前
【事务】Spring Framework核心——事务管理:ACID特性、隔离级别、传播行为、@Transactional底层原理、失效场景
java·数据库·分布式·后端·sql·spring·面试
sheji341624 分钟前
【开题答辩全过程】以 基于springboot的校园失物招领系统为例,包含答辩的问题和答案
java·spring boot·后端
程序员cxuan34 分钟前
人麻了,谁把我 ssh 干没了
人工智能·后端·程序员
wuyikeer2 小时前
Spring Framework 中文官方文档
java·后端·spring
Victor3562 小时前
MongoDB(61)如何避免大文档带来的性能问题?
后端
Victor3562 小时前
MongoDB(62)如何避免锁定问题?
后端
wuyikeer3 小时前
Spring BOOT 启动参数
java·spring boot·后端
子木HAPPY阳VIP4 小时前
Ubuntu 22.04 VMware 设置固定IP配置
人工智能·后端·目标检测·机器学习·目标跟踪
人间打气筒(Ada)4 小时前
如何基于 Go-kit 开发 Web 应用:从接口层到业务层再到数据层
开发语言·后端·golang
开心就好20254 小时前
使用Wireshark进行TCP数据包抓包分析:三次握手与四次挥手详解
后端·ios