JWT实战演示

温馨提示:jwt理论加强

第1步:创建maven项目

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lian</groupId>
    <artifactId>jwt</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jwt</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--JWT依赖,操作jwt的api-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

第2步:实际演练

package com.lian.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 1、头部(Header)
 * 头部用于描述关于该JWT的最基本的信息,例如其类型(即JWT)以及签名所用的算法(如
 * HMAC SHA256或RSA)等。这也可以被表示成一个JSON对象
 * { "alg": "HS256", "typ": "JWT" }
 * typ :是类型
 * alg :签名的算法,这里使用的算法是HS256算法
 * 对头部的json字符串进行BASE64编码
 * <p>
 * 2、负载(Payload)
 * 是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
 * ①标准中注册的声明(建议但不强制使用)
 * iss: jwt签发者
 * sub: jwt所面向的用户
 * aud: 接收jwt的一方
 * exp: jwt的过期时间,这个过期时间必须要大于签发时间
 * nbf: 定义在什么时间之前,该jwt都是不可用的.
 * iat: jwt的签发时间
 * jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
 * ②公共的声明
 * 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不
 * 建议添加敏感信息,因为该部分在客户端可解密
 * ③私有的声明
 * 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对
 * 称解密的,意味着该部分信息可以归类为明文信息。
 * 这个指的就是自定义的claim
 * <p>
 * 自定义claim跟JWT标准规定的claim区别在于:JWT规定的claim,JWT的接收方在拿到JWT之后,都知道怎么对
 * 这些标准的claim进行验证(还不知道是否能够验证);而private claims不会验证,除非明确告诉
 * 接收方要对这些claim进行验证以及规则才行
 * <p>
 * 3、签证、签名(signature)
 * jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
 * 1. header (base64加密后的) 2. payload (base64加密后的) 3. secret(盐,一定要保密)
 * 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然
 * 后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分
 * <p>
 * jwt的第1、2、3 部分均采用base64加密,然后再统一用 逗号 连接在一起生成号称 jwt
 * <p>
 * secret(盐值) 是保存在服务器端的, jwt 的签发生成也是在服务器端的, secret 就是用来进
 * 行 jwt 的签发和 jwt 的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。
 * 一旦客户端得知这个 secret , 那就意味着客户端是可以自我签发 jwt 了
 */
@SpringBootTest
public class JwtApplicationTests {

    //盐值
    private static final String secret = "yjxxt";

    /**
     * 创建token
     * token: eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJSb3NlIiwiaWF0IjoxNjQxOTgyODk3fQ.vQLsYyb756qtOEoZGWLRXpELJyeMDd-INcDrUa8pfoA
     * --------------------
     * {"alg":"HS256"}
     * {"jti":"888","sub":"Rose","iat":1641982897
     * p:�k�_
     */
    @Test
    public void createToken() {
        //创建一个JwtBuilder对象
        JwtBuilder jwtBuilder = Jwts.builder()
                //声明的标识{"jti":"888"}
                .setId("888")
                // 主体,用户{"sub":"Rose"}
                .setSubject("Rose")
                // 创建日期{"ita":"yjxxtxx"}
                .setIssuedAt(new Date())
                // 签名手段,参数1:算法,参数2:盐
                .signWith(SignatureAlgorithm.HS256, secret);
        //获取jwt的token
        String token = jwtBuilder.compact();
        System.out.println("token: " + token);
        //三部分的base64解密
        System.out.println("--------------------");
        //token是3部分分别被base64加密后再用逗号拼接在一起
        String[] split = token.split("\\.");
        System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
        System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
        //无法解密
        System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
    }

    /**
     * 解析token
     * token相比session好处是,不必所有用户信息存在服务器中,服务器只需保存个 盐值,解析token就可验证用户
     * 是由服务端进行然后发给客户端,客户端在下次向服务端发送请求时需要携带这个token(这就好像是拿着一张门票一样),
     * 那服务端接到这个token 应该解析出token中的信息(例如用户id),根据这些信息查询数据库返回相应的结果
     */
    @Test
    public void parseToken() {
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJSb3NlIiwiaWF0IjoxNjQxOTgyODk3fQ.vQLsYyb756qtOEoZGWLRXpELJyeMDd-INcDrUa8pfoA";
        //解析token获取负载中的声明对象
        Claims claims = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
        //打印声明的属性
        System.out.println("id:" + claims.getId());
        System.out.println("subject:" + claims.getSubject());
        System.out.println("issuedAt:" + claims.getIssuedAt());
    }

    /**
     * token过期校验
     * 我们并不希望签发的token是永久生效的,可以为token添加一个过期时间。原因:从服务器发出的token,服务器自己并不做记录,
     * 就存在一个弊端就是,服务端无法主动控制某token的立刻失效
     */
    @Test
    public void verify() {
        //当前系统时间的长整型
        long now = System.currentTimeMillis();
        //过期时间,这里是1分钟后的时间长整型
        long exp = now + 60 * 1000;
        //创建一个JwtBuilder对象
        JwtBuilder jwtBuilder = Jwts.builder()
                //声明的标识{"jti":"888"}
                .setId("888")
                //主体,用户{"sub":"Rose"}
                .setSubject("Rose")
                //创建日期{"ita":"yjxxtxx"}
                .setIssuedAt(new Date())
                //签名手段,参数1:算法,参数2:盐
                .signWith(SignatureAlgorithm.HS256, "yjxxt")
                //设置过期时间
                .setExpiration(new Date(exp));
        //获取jwt的token
        String token = jwtBuilder.compact();
        System.out.println(token);
    }

    /**
     * 自定义 claims
     * 以上例子只是存储了id和subject两个信息,如果你想存储更多的信息(例如角色)可以定义自定义claims
     *
     * token: 3部分 头+体+盐
     * eyJhbGciOiJIUzI1NiJ9.
     * eyJqdGkiOiI4ODgiLCJzdWIiOiJSb3NlIiwiaWF0IjoxNjQxOTg0MzQxLCJleHAiOjE2NDE5ODQ0MDEsInJvbGVzIjoiYWRtaW4iLCJsb2dvIjoibGlhbi5qcGcifQ.
     * 4gJp8BR58RDMjy095RYLX4NpqgVp8v5A5oU3SecLyQ4
     */
    @Test
    public void claims() {
        //当前系统时间的长整型
        long now = System.currentTimeMillis();
        //过期时间,这里是1分钟后的时间长整型
        long exp = now + 60 * 1000;
        //创建一个JwtBuilder对象
        JwtBuilder jwtBuilder = Jwts.builder()
                //声明的标识{"jti":"888"}
                .setId("888")
                //主体,用户{"sub":"Rose"}
                .setSubject("Rose")
                //创建日期{"ita":"yjxxtxx"}
                .setIssuedAt(new Date())
                //签名手段,参数1:算法,参数2:盐
                .signWith(SignatureAlgorithm.HS256, "yjxxt")
                //设置过期时间
                .setExpiration(new Date(exp))
                // 直接传入map // .addClaims(map)
                .claim("roles", "admin")
                .claim("logo", "lian.jpg");
        //获取jwt的token
        String token = jwtBuilder.compact();
        System.out.println("token: "+token);
    }


    /**
     * 解析自定义claims后的token
     */
    @Test
    public void claimsParse(){
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJSb3NlIiwiaWF0IjoxNjQxOTg0MzQxLCJleHAiOjE2NDE5ODQ0MDEsInJvbGVzIjoiYWRtaW4iLCJsb2dvIjoibGlhbi5qcGcifQ.4gJp8BR58RDMjy095RYLX4NpqgVp8v5A5oU3SecLyQ4";
        //解析token获取负载中的声明对象
        Claims claims = Jwts.parser().setSigningKey("yjxxt").parseClaimsJws(token).getBody();
        //打印声明的属性
        System.out.println("id:" + claims.getId());
        System.out.println("subject:" + claims.getSubject());
        System.out.println("issuedAt:" + claims.getIssuedAt());
        DateFormat sf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("签发时间:"+sf.format(claims.getIssuedAt()));
        System.out.println("过期时间:"+sf.format(claims.getExpiration()));
        System.out.println("当前时间:"+sf.format(new Date()));
        System.out.println("roles:"+claims.get("roles"));
        System.out.println("logo:"+claims.get("logo"));
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值