当前位置: 首页 > article >正文

使用 Spring Boot 实现 JWT 生成与验证的自定义类

在现代 web 应用中,JWT(JSON Web Tokens)被广泛用于用户身份验证。本文将展示如何创建一个自定义的 JWT 生成与验证类 JwtPlus,该类使用对称加密算法,并支持灵活的配置选项。我们将通过以下步骤实现这个功能:

1. 背景介绍

JWT 的结构通常包含三个部分:头部(Header)、载体(Payload)和签名(Signature)。通过对这三个部分进行编码和加密,JWT 能够安全地传递用户信息。有效的 JWT 需要在客户端和服务器之间传递,因此确保它们的安全性是至关重要的。

2. 主要功能需求

  • 生成 JWT:支持自定义 Header 和 Claims,并使用对称加密算法生成签名。
  • 验证 JWT:能够验证签名的有效性,并提取载体部分的数据。
  • 支持自定义算法:允许开发者使用不同的加密算法。

3. 实现步骤

3.1. 创建 Algorithm

该类用于定义加密算法及其密钥:

public class Algorithm {
    private String name;
    private String key;

    public Algorithm(String name, String key) {
        this.name = name;
        this.key = key;
    }

    public String getName() {
        return name;
    }

    public String getKey() {
        return key;
    }
}

3.2. 创建 JwtPlus

这是我们主要的 JWT 处理类,包含生成和验证的逻辑。

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class JwtPlus {

    private Map<String, String> header;
    private Map<String, String> claims;
    private Algorithm algorithm;

    // 私有构造函数,防止直接实例化private JwtPlus(Builder builder) {
        this.header = builder.header;
        this.claims = builder.claims;
        this.algorithm = builder.algorithm;
    }

    // 静态方法,用于获取 Builder 实例public static Builder builder() {
        return new Builder();
    }

    // 静态方法,用于获取 JwtVerifier 实例public static JwtVerifier require() {
        return new JwtVerifier();
    }

    // 生成 Token 方法public String sign() {
        if (header == null || header.isEmpty()) {
            header = new HashMap<>();
            header.put("alg", algorithm.getName());
            header.put("typ", "JWT");
        }

        String headerEncoded = encodeBase64(mapToString(header));
        String claimsEncoded = encodeBase64(mapToString(claims));

        String signature = generateSignature(headerEncoded, claimsEncoded);

        return headerEncoded + "." + claimsEncoded + "." + signature;
    }

    // 生成签名private String generateSignature(String header, String claims) {
        try {
            String data = header + "." + claims;
            Mac mac = Mac.getInstance(algorithm.getName());
            SecretKeySpec secretKeySpec = new SecretKeySpec(algorithm.getKey().getBytes(StandardCharsets.UTF_8), algorithm.getName());
            mac.init(secretKeySpec);
            byte[] signatureBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return encodeBase64(new String(signatureBytes, StandardCharsets.UTF_8));
        } catch (Exception e) {
            throw new RuntimeException("Error generating signature", e);
        }
    }

    // 内部类 JwtVerifierpublic static class JwtVerifier {
        private String secretKey;
        private Algorithm algorithm;

        public JwtVerifier secret(String secretKey) {
            this.secretKey = secretKey;
            return this;
        }

        public JwtVerifier algorithm(Algorithm algorithm) {
            this.algorithm = algorithm;
            return this;
        }

        public boolean verify(String token) {
            String[] parts = token.split("\\.");
            if (parts.length != 3) {
                return false;
            }

            String headerEncoded = parts[0];
            String claimsEncoded = parts[1];
            String signature = parts[2];

            String expectedSignature = JwtPlus.builder()
                    .claims(new HashMap<>()) // 空 claims 仅用于签名
                    .algorithm(algorithm)
                    .build()
                    .generateSignature(headerEncoded, claimsEncoded);

            return expectedSignature.equals(signature);
        }

        public Map<String, String> getClaims(String token) {
            String[] parts = token.split("\\.");
            if (parts.length != 3) {
                throw new IllegalArgumentException("Invalid Token format");
            }

            String claimsEncoded = parts[1];
            return stringToMap(decodeBase64(claimsEncoded));
        }
    }

    // 其他方法保持不变

    private static String encodeBase64(String data) {
        return Base64.getUrlEncoder().withoutPadding().encodeToString(data.getBytes(StandardCharsets.UTF_8));
    }

    private static String decodeBase64(String data) {
        return new String(Base64.getUrlDecoder().decode(data), StandardCharsets.UTF_8);
    }

    private static String mapToString(Map<String, String> map) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append(";");
        }
        return sb.toString();
    }

    private static Map<String, String> stringToMap(String token) {
        Map<String, String> map = new HashMap<>();
        String[] pairs = token.split(";");
        for (String pair : pairs) {
            String[] keyValue = pair.split("=");
            if (keyValue.length == 2) {
                map.put(keyValue[0], keyValue[1]);
            }
        }
        return map;
    }

    // Builder 类public static class Builder {
        private Map<String, String> header = new HashMap<>();
        private Map<String, String> claims = new HashMap<>();
        private Algorithm algorithm = new Algorithm("HmacSHA256", "default_secret_key");

        public Builder withHeader(Map<String, String> header) {
            this.header.putAll(header);
            return this;
        }

        public Builder claim(String key, String value) {
            this.claims.put(key, value);
            return this;
        }

        public Builder claims(Map<String, String> claims) {
            this.claims.putAll(claims);
            return this;
        }

        public Builder algorithm(Algorithm algorithm) {
            this.algorithm = algorithm;
            return this;
        }

        public JwtPlus build() {
            return new JwtPlus(this);
        }
    }
}

4. 调用示例

以下是如何使用 JwtPlusJwtVerifier 的示例:

public class TokenExample {public static void main(String[] args) {
        // 使用 builder 模式生成 Token
        Algorithm algorithm = new Algorithm("HmacSHA256", "your_secret_key");

        String token = JwtPlus.builder()
                              .claim("userId", "12345")
                              .claim("role", "admin")
                              .algorithm(algorithm)
                              .build()
                              .sign();

        System.out.println("Generated Token: " + token);

        // 使用 JwtVerifier 验证 Token
        JwtPlus.JwtVerifier verifier = JwtPlus.require()
                                              .secret("your_secret_key")
                                              .algorithm(algorithm);

        boolean isValid = verifier.verify(token);
        System.out.println("Is Token valid? " + isValid);

        // 获取 Token 中的载体部分
        Map<String, String> claims = verifier.getClaims(token);
        System.out.println("Claims: " + claims);
    }
}

5. 实现原理与方法

  1. Token 结构:JWT 由三部分组成,分别为 Header、Payload 和 Signature。Header 指定了所使用的签名算法,Payload 存放用户信息,Signature 则是使用 Header 和 Payload 生成的。
  2. 生成 JWT:
  • 使用 Builder 模式构建 JWT 对象。
  • 编码 Header 和 Payload,并生成签名。
  • 最终将三个部分通过点(.)连接成一个字符串。
  1. 验证 JWT:
  • 使用 JwtVerifier 类来验证 JWT 的有效性。
  • 验证过程包括拆分 Token,重新生成签名,并与原始签名进行比较。
  1. 灵活性:通过 Algorithm 类,可以方便地实现对称加密算法的切换,使得代码更加灵活且易于扩展。

结论

本文展示了如何使用 Java 创建一个自定义的 JWT 生成与验证类,具有灵活的算法选择和安全的签名生成。这种方法为开发现代 web 应用提供了可靠的身份验证机制,同时保持了代码的可维护性和扩展性。希望这能帮助你在项目中实现 JWT 认证!


http://www.kler.cn/a/325740.html

相关文章:

  • 领海基点的重要性-以黄岩岛(民主礁)的领海及专属经济区时空构建为例
  • 英语中do(做没做)和can(有能力/资格做)的区分
  • 数据库-Derby
  • 【东莞石碣】戴尔R740服务器维修raid硬盘问题
  • React Native 全栈开发实战班 :数据管理与状态之React Hooks 基础
  • 百度智能云 VectorDB 优势数量 TOP 1
  • golang web笔记-3.响应ResponseWriter
  • 万界星空科技数字孪生:解锁制造业未来,重塑智慧工厂新纪元
  • 从0学习React(4)---更新组件状态setState
  • gradle的入门及kotlin的了解
  • 开箱元宇宙| 探索 Great Entertainment Group 如何利用 Web3 和数字创新重新定义活动体验
  • 【Android】多角度看handler--looper的阻塞
  • 产品管理- 互联网产品(6):产品测试
  • CSS预处理器LESS
  • 工厂模式和抽象工厂模式的实验报告
  • Could not find com.mapbox.mapboxsdk:mapbox-android-accounts:0.7.0.解决
  • 禾赛嵌入式面试题及参考答案(2万字长文)
  • 升级 Windows 后如何恢复丢失的文件
  • 51单片机的教室智能照明系统【proteus仿真+程序+报告+原理图+演示视频】
  • centos7.9使用docker容器方式部署jenkins环境
  • 深度学习中的结构化概率模型 - 非结构化建模的挑战篇
  • QT 开发日志:QT 布局管理 —— 如何使用布局器组织 UI 元素
  • bash 脚本获取当前 git 所有本地分支
  • (undone) MIT6.824 Lab1
  • Python库matplotlib之三
  • 论Web性能的重要性