JWT(一):JWT(JSON Web Token)原理

大家好,我是欧阳方超,微信公众号同名。在这里插入图片描述

1 JWT(JSON Web Token)介绍

JWT(JSON Web Token)是一种开放标准(RFC7519),用于在网络应用环境中安全地传递信息。它的设计目的是紧凑且安全,特别适合于分布式站点的单点登录(SSO)场景。JWT通常用于身份验证和信息交换,能够有效地在身份提供者和服务提供者之间传递认证用户的信息,以便访问资源服务器。

1.2 JWT的组成

JWT由三部分组成,分别是Header、Payload、Signature。
Header(头部):包含令牌的元数据,通常包括签名算法(alg)和类型(type)。例如:

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

这个 JSON 对象会经过 Base64Url 编码,成为 JWT 的第一部分。

Payload(载荷):存放实际需要传递的数据,可以包含一些官方标准声明如iss(签发人)、exp(过期时间)、sub(主题)等,也可以包含自定义字段。注意,载荷部分的数据是未加密的,因此不能包含敏感数据。

   {
       "sub": "1234567890",
       "name": "John Doe",
       "admin": true
   }

这些信息也是 JSON 对象,经过 Base64Url 编码后构成 JWT 的第二部分。

Signature(签名):签名是通过对头部和载荷使用指定的签名算法和密钥来计算的。例如,对于HS256算法,会使用一个密钥对头部和载荷的组合进行HMAC-SHA256计算得到签名。它用于验证消息在传递过程中没有被篡改,并且还可以验证发送者的身份(如果密钥是保密的)。

String signature = HMACSHA256(encodedString, secret);

这三部分通过.连接形成最终的JWT字符串,如:header.payload.signature。

1.3 生成 JWT(Token)的流程

步骤一:构建头部和载荷
首先,应用程序确定要包含在 JWT 中的信息,即构建头部和载荷部分。这可能包括用户身份信息、权限信息、过期时间等。
例如,一个简单的用户认证场景,头部可能指定算法为 HS256,载荷包含用户 ID 和用户名等信息。
步骤二:编码头部和载荷
将构建好的头部和载荷的 JSON 对象分别进行 Base64Url 编码。Base64Url 编码是一种对 Base64 编码的变体,它使编码后的字符串更适合在 URL 和文件名等场景中使用,因为它去掉了 Base64 编码中的一些特殊字符(如 “+”、“/” 和 “=”)。
步骤三:生成签名
使用选定的签名算法(如 HS256)和密钥,对编码后的头部和载荷进行签名计算。如果是 HS256 算法,会对头部和载荷的组合(用 “.” 连接)进行 HMAC - SHA256 计算。
例如,假设头部编码后为headerEncoded,载荷编码后为payloadEncoded,密钥为secretKey,那么签名signature的计算方式大致为:signature = HMAC - SHA256(secretKey, headerEncoded + ‘.’ + payloadEncoded)。
步骤四:组合成 JWT
最后,将编码后的头部、载荷和签名用 “.” 连接起来,形成最终的 JWT,格式为header.payload.signature。这个 JWT 可以被发送给客户端,例如在用户登录成功后作为身份认证令牌发送。

1.4 验证 JWT 的流程

步骤一:拆分 JWT
当客户端发送 JWT 到服务器进行验证时,服务器首先将 JWT 按照 “.” 拆分为头部、载荷和签名三个部分。
步骤二:解码头部和载荷
对拆分出的头部和载荷部分进行 Base64Url 解码,得到原始的 JSON 对象。这样可以获取其中包含的信息,如令牌类型、签名算法、用户信息等。
步骤三:重新计算签名
根据解码后的头部信息获取签名算法,使用与生成 JWT 时相同的密钥,按照相同的签名算法对解码后的头部和载荷进行重新签名计算。
例如,如果头部中的算法是 HS256,密钥是secretKey,头部解码后为headerDecoded,载荷解码后为payloadDecoded,那么重新计算签名newSignature的方式大致为:newSignature = HMAC - SHA256(secretKey, headerDecoded + ‘.’ + payloadDecoded)。
步骤四:验证签名
将重新计算得到的签名与接收到的签名进行比较。如果两者相同,说明 JWT 在传输过程中没有被篡改,并且是由拥有正确密钥的一方生成的。此时可以信任 JWT 中的信息,并根据其中的载荷信息(如用户权限等)进行后续的操作,如授权访问资源。如果签名不匹配,则说明 JWT 可能被篡改或者密钥不正确,应该拒绝该 JWT。

1.5 JWT使用场景

授权:用户登录后,每次请求中包含JWT,以允许用户访问被授权的资源。
信息交换:安全地在各方之间传递信息,因其可以被签名以验证发件人身份和内容完整性。

2 JJWT——JWT的实现之一

JJWT 是一个专门为 Java 开发者设计的库,用于方便地创建、解析和验证 JWT,旨在提供简单易用的API。它支持在JVM和Android环境中使用,并遵循JWT、JWS、JWE等RFC规范。JJWT是一个开源项目,可以通过maven引入依赖:

<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jwt.version}</version>
        </dependency>

2.1 JJWT的基本使用

2.1.1 生成JWT

以下是一个简单的使用JJWT生成JWT的示例代码:

public static String createJWT(Map<String, Object> header, Map<String, Object> claims, String subject, long ttlMillis) {
        return Jwts.builder()
                .setHeader(header)
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + ttlMillis))
                .signWith(SignatureAlgorithm.HS256, SECRET)
                .compact();
    }

上面的方法创建了一个带有声明、主题、生成时间及有效期的JWT,并使用HMAC SHA512算法进行签名。

2.1.2 解析和验证JWT

以下示例代码演示了如何解析和验证JWT:

public static void parseJWT(String jwt) {
        try {
            Claims body = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(jwt).getBody();
            System.out.println(body);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在解析时,使用 Jwts.parser() 创建一个解析器,通过 setSigningKey() 设置正确的密钥,然后使用 parseClaimsJws() 方法解析 JWT。如果签名验证成功,将返回 Claims 对象,其中包含了载荷中的信息;如果签名验证失败或者 JWT 格式不正确等,将抛出异常。

下面是完整的示例:

import io.jsonwebtoken.*;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtil {
    private static final String SECRET = "a3f5e2c8b6d1e9f0c4a7b8d9e5c3f2a1b0e6d4c7f8a9b0c1d2e3f4a5b6c7d8e9";
    private static final String SECRET1 = "your_secret_key1";
    public static void main(String[] args) {
        //定义头部
        HashMap<String, Object> headers = new HashMap<>();
        headers.put("alg", "HS512");
        headers.put("type", "JWT");
        //定义载荷
        HashMap<String, Object> claims = new HashMap<>();
        claims.put("user_id", 123);
        claims.put("username", "peter");
        //定义主题
        String subject = "subject";
        //定义存活时间为一个小时
        long ttlMillis = 1 * 60 * 60 * 1000;

        String jwt = createJWT(headers, claims, subject, ttlMillis);
        System.out.println("JWT: " + jwt);

        parseJWT(jwt);
    }

    public static String createJWT(Map<String, Object> header, Map<String, Object> claims, String subject, long ttlMillis) {
        return Jwts.builder()
                .setHeader(header)
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + ttlMillis))
                .signWith(SignatureAlgorithm.HS256, SECRET)
                .compact();
    }

    public static void parseJWT(String jwt) {
        try {
            Claims body = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(jwt).getBody();
            System.out.println(body);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3 总结

JWT 是开放标准,由 Header、Payload、Signature 组成,用于安全传递信息,适用于身份验证与信息交换等场景。JJWT 是其 Java 实现库,可便捷创建、解析和验证 JWT,如通过特定方法生成并验证,遵循相关规范且开源,在 Java 开发中有重要应用。

我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值