【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;
    }



}

相关推荐
程序员志哥4 小时前
手把手带你使用JWT实现单点登录
springboot
小沈同学呀17 小时前
【实战】EasyExcel实现百万级数据导入导出
java·大数据·spring·springboot·mybatisplus·easyexcel
程序员志哥1 天前
SpringBoot+mail 轻松实现各类邮件自动推送
springboot
宁波阿成3 天前
SpringUtils.getBean 空指针异常问题
java·springboot·flowable·jeecg-boot
飞翔的佩奇4 天前
SpringBoot3基础用法
java·开发语言·spring·maven·框架·ssm·springboot
代码匠心4 天前
从零开始学Spring Boot系列-集成Spring Security实现用户认证与授权
java·后端·springboot·spring security
赢乐6 天前
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单
中间件·node.js·express·jwt·session·cors·单点登录sso
Pleasure12346 天前
我给我的学校写了个校园墙小程序
java·vue.js·spring boot·小程序·vue·springboot
吃海的虾酱6 天前
【JAVA】利用Redisson和Spring实现高效物联温度控制链路,确保温度调节的准确性和效率,定时链路执行使用案例,一环扣一环
java·分布式·后端·spring·spring cloud·bootstrap·springboot
qq_35323353897 天前
【原创】springboot+mysql海鲜商城设计与实现
java·mysql·mvc·springboot·web