【JWT】入门 *JWT*,并封装一个实用的 *JWT* 工具类

个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~

个人主页:.29.的博客

学习社区:进去逛一逛~

【JWT】入门 *JWT*,并封装一个实用的 *JWT* 工具类

    • [1. 什么是JSON Web Token(JWT)?](#1. 什么是JSON Web Token(JWT)?)
    • [2. JWT使用场景](#2. JWT使用场景)
    • [3. JWT结构](#3. JWT结构)
      • [⚪页眉 Header](#⚪页眉 Header)
      • [⚪有效载荷 Payload](#⚪有效载荷 Payload)
      • [⚪签名 Signature](#⚪签名 Signature)
      • 一个完整的JWT长什么样?
    • [4. JWT基本使用(SpringBoot项目中)](#4. JWT基本使用(SpringBoot项目中))
    • [5. 封装一个实用的JWT工具类](#5. 封装一个实用的JWT工具类)

1. 什么是JSON Web Token(JWT)?

JSON Web 令牌(JWT)

JSON Web 令牌 (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且独立的方式,用于将信息作为 JSON 对象在各方之间安全地传输 。此信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSAECDSA 的公钥/私钥对进行签名。

2. JWT使用场景

JWT使用场景

  • 授权:这是使用 JWT 的最常见方案。用户登录后,每个后续请求都将包含 JWT,从而允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够轻松地跨不同域使用。
  • 信息交换:JSON Web 令牌是在各方之间安全传输信息的好方法。由于 JWT 可以签名(例如,使用公钥/私钥对),因此您可以确定发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。

3. JWT结构

JSON Web Token 结构

JWT由三部分组成,分别是页眉(Header)、有效载荷(Payload)、签名(Signature) ,他们之间由符号.进行分割。

  • Header
  • Payload
  • Signature

因此,我们可知,JWT通常是这样的:ddddd.hhhhh.jjjjj

⚪页眉 Header


  • Header通常由两部分组成:令牌类型typ、使用的签名算法alg;
json 复制代码
{
  "alg": "HS256",
  "typ": "JWT"
}

然后,这条JSON数据会经过Base64Url编码,组成JWT的第一部分(Header)

⚪有效载荷 Payload


JWT的第二部分是Payload,其中包含的是 Claims(声明), Claims是关于用户实体和其他数据的陈述。

有三种类型的Claims:registered claims public claimsprivate claims

  • **已注册的声明(registered claims):**是一组预定义的声明,这些声明不是强制性的,但建议提供一组有用的、可互操作的声明。比如:iss (发行人)、exp (到期时间)、sub (主题)、aud(受众)等...

    注意,上述提到的声明,名称的长度都是简短的几个字符,因为 JWT 应该是紧凑的。

  • 公共声明(public claims):这些声明可以由使用 JWT 的人随意定义。但为了避免冲突,应在 IANA JSON Web Token注册表中定义它们,或者将其定义为包含抗冲突命名空间的 URI。

  • 私人声明(private claims) :这些声明是为了让同意使用它们的各方之间共享信息而创建的自定义声明,既不是 已注册 声明,也不是 公共 声明。

例如

json 复制代码
{
  "sub": "123456789",
  "name": ".29.",
  "admin": true
}

接下来,这条JSON数据会经过Base64Url编码,组成JWT的第二部分(Payload)

请注意,对于已签名的令牌,此信息虽然受到保护,但任何人都可以读取。不要将机密信息放在 JWT 的有效负载或标头元素中,除非它是加密的。

⚪签名 Signature


要创建签名部分,必须获取经过Base64Url 编码后的标头、经过Base64Url编码后的有效负载、密钥、标头中指定的算法,并对其进行签名。

例如,如果要使用 HMAC SHA256 算法,将按以下方式创建签名:

java 复制代码
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

一个完整的JWT长什么样?

  • 下述JWT对Header 和 Payload 进行了Base64Url 编码,并使用密钥进行了签名,三个三个 Base64-URL 字符串,由点.进行分隔。
  • 它可以在 HTML 和 HTTP 环境中轻松传递,同时与基于 XML 的标准(如 SAML)相比更紧凑。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkiLCJuYW1lIjoiLjI5LiIsImFkbWluIjp0cnVlfQ.1M3o41CutZL1fTjEftuxs6g5ug5M-j6GcP_K61nAIjM


4. JWT基本使用(SpringBoot项目中)

导入maven坐标

xml 复制代码
        <!-- jjwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <!-- jaxb-api -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- jaxb-impl -->
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- jaxb-api -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>

测试基本使用

java 复制代码
/**
 * @author .29.
 * @create 2024-03-15 23:37
 */
@SpringBootTest
public class jwtTest {
    //签名密钥
    String signature = "admin";

    /**
     * 加密生成JWT的测试方法
     */
    @Test
    public void jwt(){
        JwtBuilder builder = Jwts.builder(); //获取JWT生成器
        //使用JWT生成器创建一个JWT
        String jwtToken = builder
                //Header
                .setHeaderParam("typ", "JWT")//类型
                .setHeaderParam("alg", "HS256")//使用的算法
                //Payload
                .claim("name", ".29.")
                .claim("role", "admin")
                .setSubject("admin-test")
                .setExpiration(new Date(System.currentTimeMillis() + 1000*60*60*24))//失效日期:当前时间+24小时
                .setId(UUID.randomUUID().toString())
                //signature
                .signWith(SignatureAlgorithm.HS256, signature)//使用的算法+签名密钥
                //调用compact()方法将三部分拼接起来并用'.'分隔
                .compact();
        System.out.println("生成的JWT:" + jwtToken);
    }

    /**
     * 解密JWT获取信息的测试方法
     */
    @Test
    public void parse(){
        //jwt方法加密出来的密钥
        String jwtToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiLjI5LiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE3MTA2MDUyMjYsImp0aSI6IjFmNTkwNmNjLWQ4MWMtNGQ0MS1hYmJiLWY2M2NkZTg5OTM0ZSJ9.sgkpuvWTyzwDbwmUeKjQt2IuuL2zG0NKrQofdItbBAU";
        JwtParser parser = Jwts.parser();//获取JWT解密工具
        Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(jwtToken); //根据签名密钥对JWT进行解密
        Claims claims = claimsJws.getBody();
        //获取解密出来的载荷内容
        System.out.println("用户名:" + claims.get("name") + "\n"
                        + "规则:" + claims.get("role") + "\n"
                        + "主题:" + claims.getSubject() + "\n"
                        + "ID:" + claims.getId() + "\n"
                        + "失效时间(有效期至):" + claims.getExpiration()
        );
    }
}

jwt()输出:👇

生成的JWT:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiLjI5LiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE3MTA2MDUzNzUsImp0aSI6IjYxYzUyNmQ1LTJkMDQtNGE1YS1hZWRmLTQwMDE5ZTc1ZmFmOSJ9.hhacIWFVfLJkDpNLlIHokShtNI1M_CTHUqKSIIUrfIw

/

parse()输出:👇

用户名:.29.

规则:admin

主题:admin-test

ID:1f5906cc-d81c-4d41-abbb-f63cde89934e

失效时间(有效期至):Sun Mar 17 00:07:06 CST 2024


5. 封装一个实用的JWT工具类

工具类JwtUtil.java:

java 复制代码
/**
 * @author .29.
 * @create 2024-03-16 0:29
 * @description 生成JwtToken, 获取JwtToken中加密的信息, 判断JwtToken是否合法
 *
 */
public class JwtUtil {
    //创建默认的密钥与加密算法,提供给空参构造器调用
    private static final String defaultBase64EncodingSecretKey = "eb^29*be";
    private static final SignatureAlgorithm defaultSignatureAlgorithm = SignatureAlgorithm.HS256;

    public JwtUtil(){
        this(defaultBase64EncodingSecretKey, defaultSignatureAlgorithm);
    }

    private final String base64EncodingSecretKey;
    private final SignatureAlgorithm signatureAlgorithm;

    public JwtUtil(String base64EncodingSecretKry, SignatureAlgorithm signatureAlgorithm){
        this.base64EncodingSecretKey = base64EncodingSecretKry;
        this.signatureAlgorithm = signatureAlgorithm;
    }

    public String encoding(String issuer, long ttlMillis, Map<String, Object> claims){
        // iss签发人,ttlMillis生存时间,claims是指还想要在jwt中存储的一些非隐私信息
        if(claims == null){
            claims = new HashMap<>();
        }
        long nowMillis = System.currentTimeMillis();
        JwtBuilder builder = Jwts.builder()
                .setId(UUID.randomUUID().toString())
                .setSubject(issuer)
                .setClaims(claims)
                .setIssuedAt(new Date(nowMillis))
                .signWith(signatureAlgorithm, base64EncodingSecretKey);
        if(ttlMillis > 0){
            long exp = nowMillis + ttlMillis;
            Date date = new Date(exp);
            builder.setExpiration(date);
        }
        return builder.compact();
    }

    public Claims decoding(String jwtToken){
        JwtParser parser = Jwts.parser();
        Jws<Claims> claimsJws = parser.setSigningKey(base64EncodingSecretKey).parseClaimsJws(jwtToken);
        return claimsJws.getBody();
    }

    // 判断jwtToken是否合法
    public boolean isVerify(String jwtToken) {
        // 这个是官方的校验规则,这里只写了一个"校验算法",可以自己加
        Algorithm algorithm = null;
        switch (signatureAlgorithm) {
            case HS256:
                algorithm = Algorithm.HMAC256(Base64.decodeBase64(base64EncodingSecretKey));
                break;
            default:
                throw new RuntimeException("不支持该算法");
        }
        JWTVerifier verifier = JWT.require(algorithm).build();
        verifier.verify(jwtToken);
        // 校验不通过会抛出异常
        // 判断合法的标准:1. 头部和荷载部分没有篡改过。2. 没有过期
        return true;
    }



}

相关推荐
郑洁文16 小时前
旅游景点推荐系统的设计与实现
springboot·毕设·旅游系统·旅游景点推荐系统
ANnianStriver21 小时前
PetLumina-AI 驱动的宠物生活管理平台
java·生活·vue3·springboot·ai编程·宠物·全栈开发
YDS8291 天前
DeepSeek RAG&MCP + Agent智能体项目 —— 集成ELK日志管理系统和Prometheus监控系统
java·elk·ai·springboot·agent·prometheus·deepseek
疯狂SQL1 天前
JWT 在线解码、验签、生成一篇讲透:附前端实现、工具架构与在线体验地址
javascript·jwt·编解码·jwt测试
曲幽2 天前
FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好
python·fastapi·web·jwt·oauth2·user·authentication
极光代码工作室3 天前
基于SpringBoot的任务管理系统
java·springboot·web开发·后端开发
西凉的悲伤3 天前
Spring Security + JWT 登录认证完整实践指南
java·后端·spring·spring security·jwt
杭州杭州杭州3 天前
瑞吉外卖项目
springboot
逍遥德4 天前
Java编程高频的“技术点”-03:“下划线命名”参数,后端用“驼峰命名“接收
java·后端·springboot
弹简特4 天前
【Java项目-轻聊】08-用户管理模块-实现获取用户信息+头像上传+显示头像
java·开发语言·springboot