JWT解密:探秘令牌魔法与Java的完美交互

本文介绍了JWT(JSONWebToken)的基本概念、认证流程、结构组成(Header、Payload和Signature),以及如何在Java中使用Auth0库实现JWT的创建、验证和解码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JWT 简介

JWT 简称 JSON Web Token,也就是通过 JSON 形式作为 Web 应用中的令牌,用于各方之间安全地将信息作为 JSON 对象传输,在数据传输的过程中还可以完成数据加密、签名等相关处理。

注意:JWT 的三个部分的 Header 和 Payload都是明文存储!只不过内容通过 Base64 转码了!所以不要将重要信息存储在 JWT 中!

认证流程

  1. 首先,前端通过 Web 表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个 HTTP POST 请求。建议的方式是通过 SSL 加密的传输(HTTPS),从而避免敏感信息被嗅探。
  2. 后端核对用户名和密码成功后,将用户的 ID 等其他信息作为 JWT Payload(负载),将其与头部分别进行 Base64 编码拼接后签名,形成一个 Token。形成的 Token 就是一个形同 head.payload.singurater 的字符串。
  3. 后端将 Token 字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在 localStorage 或 sessionStorage 上,退出登录时前端删除保存的 Token 即可。
  4. 前端在每次请求时将 Token 放入 HTTP Header 中的 Authorization 位。
  5. 后端检查是否存在,如存在验证 Token 的有效性。例如,检查签名是否正确;检查 Token 是否过期;检查 Token 的接收方是否是自己(可选)。
  6. 证通过后后端使用 Token 中包含的用户信息进行其他逻辑操作,返回相应结果。

JWT 结构

JWT 的组成有三部分:标头(Header)、有效载荷(Payload)、签名(Signature)。

Header(Base64Url).Payload(Base64Url).Secret(Header(Base64Url)+ Payload(Base64Url))

标头 Header

标头通常由两部分组成:令牌的类型和所使用的签名算法,例如 HMAC SHA256(默认)或 RSA。它会使用 Base64 编码组成 JWT 结构的第一部分。

注意:Base64 是一种编码,也就是说,它是可以被翻译回原来的样子的,它并不是一种加密过程。

有效载荷 Payload

将能用到的用户信息放在 Payload 中。官方建议不要放特别敏感的信息,例如密码。

签名 Signature

签证由三部分组成,header 和 payload 分别经 Base64Url(一种在 Base64 上做了一点改变的编码)编码后由 . 连接,服务器生成秘钥(secret),连接之后的字符串在经 Header 中声明的加密方式和秘钥加密,再用 . 和加密前的字符串连接。服务器验证 Token 时只会验证第三部分。

使用JWT

导入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.0</version>
</dependency>

创建工具类

package world.xuewei;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

public class JwtUtils {

    /**
     * 密钥
     */
    private static final String SECRET = "xuewei";

    /**
     * 获取Token,默认过期时间为1天
     */
    public static String getToken(Map<String, String> map) {
        return getToken(map, Calendar.DATE, 1);
    }

    /**
     * 获取Token,指定过期时间
     */
    public static String getToken(Map<String, String> map, Integer calenderType, Integer count) {
        // 创建令牌的过期时间
        Calendar instance = Calendar.getInstance();
        instance.add(calenderType, count);
        // 创建令牌建造者,封装请求参数
        JWTCreator.Builder builder = JWT.create();
        map.forEach(builder::withClaim);
        // 设置过期时间
        builder.withExpiresAt(instance.getTime());
        // 设置加密算法和密钥
        return builder.sign(Algorithm.HMAC256(SECRET));
    }

    /**
     * 验证Token的合法性
     *
     * @throws TokenExpiredException          Token过期
     * @throws SignatureVerificationException 密钥不匹配
     * @throws AlgorithmMismatchException     算法不匹配
     * @throws JWTDecodeException             Token异常
     */
    public static DecodedJWT validate(String token) {
        return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
    }

    /**
     * 获取Token中包含的信息
     *
     * @throws TokenExpiredException          Token过期
     * @throws SignatureVerificationException 密钥不匹配
     * @throws AlgorithmMismatchException     算法不匹配
     * @throws JWTDecodeException             Token异常
     */
    public static Map<String, String> getPayloadMap(String token) {
        DecodedJWT verify = validate(token);
        Map<String, Claim> claims = verify.getClaims();
        // 转换为标准的Map
        Map<String, String> result = new HashMap<>();
        claims.forEach((k, v) -> {
            result.put(k, v.asString());
        });
        return result;
    }
}

测试代码

package world.xuewei;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.junit.Test;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

public class JwtTest {

    /**
     * 创建 Token
     */
    @Test
    public void testCreateToken() {
        // Payload Map
        Map<String, String> payload = new HashMap<>();
        payload.put("userId", "1001");
        payload.put("roleCode", "admin");
        // 创建默认时效 Token 1天
        String defaultToken = JwtUtils.getToken(payload);
        System.out.println("defaultToken = " + defaultToken);
        // 创建自定义时效 Token 1 分钟
        String customToken = JwtUtils.getToken(payload, Calendar.MINUTE, 1);
        System.out.println("customToken = " + customToken);
    }

    /**
     * 验证 Token
     */
    @Test
    public void testValidateToken() {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlQ29kZSI6ImFkbWluIiwiZXhwIjoxNjkyMTczMDk4LCJ1c2VySWQiOiIxMDAxIn0.P7Qc0agYIkuB3XuLs76tuxb9Z10a75srffwATzM45Nw";
        try {
            JwtUtils.validate(token);
        } catch (TokenExpiredException e) {
            throw new RuntimeException("Token 已过期");
        } catch (SignatureVerificationException e) {
            throw new RuntimeException("Token 密钥不匹配");
        } catch (AlgorithmMismatchException e) {
            throw new RuntimeException("Token 算法不匹配");
        } catch (JWTDecodeException e) {
            throw new RuntimeException("Token 异常");
        }
        System.out.println("Token 验证通过");
    }

    /**
     * 解析 Token
     */
    @Test
    public void testParseToken() {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlQ29kZSI6ImFkbWluIiwiZXhwIjoxNjkyMTczMDk4LCJ1c2VySWQiOiIxMDAxIn0.P7Qc0agYIkuB3XuLs76tuxb9Z10a75srffwATzM45Nw";
        // 此方法中存在 Token 有效性验证的逻辑,故也需要异常捕获处理
        Map<String, String> payloadMap;
        try {
            payloadMap = JwtUtils.getPayloadMap(token);
        } catch (TokenExpiredException e) {
            throw new RuntimeException("Token 已过期");
        } catch (SignatureVerificationException e) {
            throw new RuntimeException("Token 密钥不匹配");
        } catch (AlgorithmMismatchException e) {
            throw new RuntimeException("Token 算法不匹配");
        } catch (JWTDecodeException e) {
            throw new RuntimeException("Token 异常");
        }
        System.out.println("payloadMap = " + payloadMap);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值