玩转 SpringBoot 2 之整合 JWT 上篇

前言

该文主要带你了解什么是 JWT,以及JWT 定义和先关概念的介绍,并通过简单Demo 带你了解如何使用 SpringBoot 2 整合?JWT。
介绍前在这里我们来探讨一下如何学习一门新的技术,我个人总结为 RSA

  1. R:read 去读官方文档 。
  2. S:search 谷歌或百度先关技术文章或 github 去搜索先关信息。
  3. A:ask 可以向技术大牛请教或和自己的同事同学进行探讨。

关于?RSA 仅仅代码个人的学习观点,只是给读者一个不成熟的小建议哈

JWT 介绍

官网介绍如下:

What is JSON Web Token?
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the?HMAC?algorithm) or a public/private key pair using?RSA?or?ECDSA.
Although JWTs can be encrypted to also provide secrecy between parties, we will focus on?signed?tokens. Signed tokens can verify the?integrity?of the claims contained within it, while encrypted tokens?hide?those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

中文翻译:
JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于在各方之间作为JSON对象安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。JWTS可以使用秘密(使用HMAC算法)或公钥/私钥对使用RSA或ECDSA来签名。
虽然JWTS可以加密,但也提供保密各方之间,我们将重点放在签名令牌。签名的令牌可以验证包含在其中的声明的完整性,而加密的令牌隐藏这些声明以防其他各方。当令牌使用公钥/私钥对签名时,签名也证明只有持有私钥的方才是签名的方。

JWT 先关概念介绍

使用 JWT?前需要先了解三块内容:

  1. 头部信息
  2. 载荷信息
  3. 签名信息

头部信息

?头部信息由2部分组成

  1. 令牌的类型,即 JWT
  2. 使用的签名算法 ,例如HMACSHA256或RSA。

头部信息 JSON 代码如下:

{
  "alg": "HS256",
  "typ": "JWT"
}

然后,这个JSON被编码为?Base64Url,形成 JWT 的第一部分。

载荷信息

其中包含声明(claims),声明可以存放实体(通常是用户)和其他数据的声明,声明包括3种类型

  1. 已注册声明
  2. 公开声明
  3. 私有声明

已注册声明
这些是一组预定义声明,不是强制性的,但建议使用,以提供一组有用的,可互操作的声明。其中一些是:?iss(发行人),?exp(到期时间),sub(主题),?aud(观众)

公开声明
可以参考?IANA JSON Web令牌注册表https://www.iana.org/assignments/jwt/jwt.xhtml) 查看公共的声明

私有声明
根据根据自己的业务需要自定义的一些数据格式。
示例有效负载可以是:

{

"sub": "1234567890",

"name": "John Doe",

"admin": true

}

签名信息

?这个部分需要 base64 加密后的 头部信息(header) 和 base64 加密后的载荷信息(payload),使用连接组成的字符串,然后通过头部信息(header)中声明的加密方式进行加盐 secret 组合在加密,然后就构成了 JWT 的第三部分。
例如,如果要使用HMAC SHA256算法,将按以下方式创建签名:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

JWT 简单实用演示介绍

首先去 JWT 官网一探究竟,访问?https://jwt.io?进入 JWT 官网。接下来我们开始学习如果使用JWT,通过点击上图中?LEARN MORE ABOUT JWT 显示如下图:然后点击START USING THE TOOL。

[图片]

[图片]

如下图所示 选择对应的语言的 JWT 使用教程。我们这里介绍是是 标注有:maven:com.auth0 java-jwt 的版本。

查看 JWT?GitHub 示例教程和源码。?https://github.com/auth0/java-jwt

在README.md 文件中有使用介绍和示例程序。

GitHub 上的示例相对简单些,接下来我带大家写一个相对详细的Demo。

第一步在SpringBoot 应用中引入JWT 依赖到pom.xml中

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

我们通过三个方法来演示如何使用JWT

  1. createToken() 创建不携带自定义信息的 token
  2. createTokenWithClaim() 创建携带自定义信息的 token
  3. verifyToken() 验证我们的token信息并解析token中的内容

    生成不携带自定义信息 JWT token

    第一步:构建头部信息

Map<String, Object> map = new HashMap<String, Object>();
map.put("alg", "HS256");
map.put("typ", "JWT");

第二步:构建密钥信息?

Algorithm algorithm = Algorithm.HMAC256("secret");

?第三步:我们通过定义注册和自定义声明 并组合头部信息和密钥信息生成jwt token

String token = JWT.create()
   .withHeader(map)// 设置头部信息 Header
   .withIssuer("SERVICE")//设置 载荷 签名是有谁生成 例如 服务器
   .withSubject("this is test token")//设置 载荷 签名的主题
   // .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的.
   .withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的
   .withIssuedAt(nowDate) //设置 载荷 生成签名的时间
   .withExpiresAt(expireDate)//设置 载荷 签名过期的时间
   .sign(algorithm);//签名 Signature

详细代码如下:
```java
@Test
public void createToken() {

    String secret = "secret";// token 密钥
    Algorithm algorithm = Algorithm.HMAC256("secret");

    // 头部信息
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("alg", "HS256");
    map.put("typ", "JWT");

    Date nowDate = new Date();
    Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期

    String token = JWT.create()
        .withHeader(map)// 设置头部信息 Header
        .withIssuer("SERVICE")//设置 载荷 签名是有谁生成 例如 服务器
        .withSubject("this is test token")//设置 载荷 签名的主题
        // .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的.
        .withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的
        .withIssuedAt(nowDate) //设置 载荷 生成签名的时间
        .withExpiresAt(expireDate)//设置 载荷 签名过期的时间
        .sign(algorithm);//签名 Signature
    Assert.assertTrue(token.length() > 0);
}
```

生成携带自定义信息 JWT token

自定义信息通过?withClaim 方法进行添加,具体操作如下:
java JWT.create() .withHeader(map) .withClaim("loginName", "zhuoqianmingyue") .withClaim("userName", "张三") .withClaim("deptName", "技术部")
生成携带自定义信息 JWT token 详细代码如下:

    @Test
    public String createTokenWithChineseClaim() {

        Date nowDate = new Date();
        Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        Algorithm algorithm = Algorithm.HMAC256("secret");
        String token = JWT.create().withHeader(map)
                /* 设置 载荷 Payload */
                .withClaim("loginName", "zhuoqianmingyue").withClaim("userName", "张三").withClaim("deptName", "技术部")
                .withIssuer("SERVICE")// 签名是有谁生成 例如 服务器
                .withSubject("this is test token")// 签名的主题
                // .withNotBefore(new Date())//定义在什么时间之前,该jwt都是不可用的
                .withAudience("APP")// 签名的观众 也可以理解谁接受签名的
                .withIssuedAt(nowDate) // 生成签名的时间
                .withExpiresAt(expireDate)// 签名过期的时间
                /* 签名 Signature */
                .sign(algorithm);

        Assert.assertTrue(token.length() > 0);
        return token;

验证 JWT Token

第一步:构建密钥信息

 Algorithm algorithm = Algorithm.HMAC256("secret");

?第二步:通过密钥信息和签名的发布者的信息生成JWTVerifier (JWT验证类)?
java JWTVerifier verifier = JWT.require(algorithm) .withIssuer("SERVICE") .build(); Algorithm algorithm = Algorithm.HMAC256("secret");

不添加?.withIssuer("SERVICE") 也是可以获取 JWTVerifier?。

第三步:通过JWTVerifier 的verify获取 token中的信息。?
java DecodedJWT jwt = verifier.verify(token);

如下面代码所示就可以获取到我们之前生成 token 的 签名的主题,观众 和自定义的声明信息。

String subject = jwt.getSubject();
List<String> audience = jwt.getAudience();
Map<String, Claim> claims = jwt.getClaims();
for (Entry<String, Claim> entry : claims.entrySet()) {
   String key = entry.getKey();
   Claim claim = entry.getValue();
   System.out.println("key:"+key+" value:"+claim.asString());
}

验证 JWT Token 详细代码如下:
```java
@Test
public void verifyToken() throws UnsupportedEncodingException {
String token = createTokenWithChineseClaim2();

    Algorithm algorithm = Algorithm.HMAC256("secret");
    JWTVerifier verifier = JWT.require(algorithm).withIssuer("SERVICE").build(); // Reusable verifier instance
    DecodedJWT jwt = verifier.verify(token);

    String subject = jwt.getSubject();
    List<String> audience = jwt.getAudience();
    Map<String, Claim> claims = jwt.getClaims();
    for (Entry<String, Claim> entry : claims.entrySet()) {
        String key = entry.getKey();
        Claim claim = entry.getValue();
        log.info("key:" + key + " value:" + claim.asString());
    }
    Claim claim = claims.get("loginName");

    log.info(claim.asString());
    log.info(subject);
    log.info(audience.get(0));

}
public String createTokenWithChineseClaim2() throws UnsupportedEncodingException {

    Date nowDate = new Date();
    Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期

    Map<String, Object> map = new HashMap<String, Object>();
    map.put("alg", "HS256");
    map.put("typ", "JWT");

    User user = new User();
    user.setUserNaem("张三");
    user.setDeptName("技术部");
    Gson gson = new Gson();
    String userJson = gson.toJson(user);

    String userJsonBase64 = BaseEncoding.base64().encode(userJson.getBytes());

    Algorithm algorithm = Algorithm.HMAC256("secret");
    String token = JWT.create().withHeader(map)

            .withClaim("loginName", "zhuoqianmingyue").withClaim("user", userJsonBase64).withIssuer("SERVICE")// 签名是有谁生成
            .withSubject("this is test token")// 签名的主题
            // .withNotBefore(new Date())//该jwt都是不可用的时间
            .withAudience("APP")// 签名的观众 也可以理解谁接受签名的
            .withIssuedAt(nowDate) // 生成签名的时间
            .withExpiresAt(expireDate)// 签名过期的时间
            .sign(algorithm);//签名 Signature

    return token;
}
```

小结

JWT 就是一个生成 Token 的工具,如果不使用 JWT 我们也可以根据自己加密规则生成 Token。只不过?JWT 规范了生成 Token 定义了一个标准而已。JWT 的核心的功能就是:生成Token、解析Token。在 玩转 SpringBoot 2 之整合 JWT 下篇中将带大家通过一个接口登录的案例简单介绍 JWT 实战操作。

代码示例

具体代码示例请查看我的GitHub 仓库 springbootexamples 中的 spring-boot-2.x-jwt?下 src/test/java JWTDemo.java文件。

GitHub:https://github.com/zhuoqianmingyue/springbootexamples

参考文献

https://jwt.io/introduction/
https://github.com/auth0/java-jwt

原文地址:https://www.cnblogs.com/jerry126/p/11621352.html

时间: 2024-08-30 10:34:51

玩转 SpringBoot 2 之整合 JWT 上篇的相关文章

玩转 SpringBoot 2 之整合 JWT 下篇

前言 在<玩转 SpringBoot 2 之整合 JWT 上篇> 中介绍了关于 JWT 相关概念和JWT 基本使用的操作方式.本文为 SpringBoot 整合 JWT 的下篇,通过解决 App 用户登录 Session 问题的实战操作,带你更深入理解 JWT.通过本文你还可以了解到如下内容: SpringBoot 使用拦截器的实际应用 SpringBoot 统一异常处理 SpringBoot 快速搭建 RESTful Api 关于生成JWT 操作请参考 <玩转 SpringBoot 2

玩转 SpringBoot 2 快速整合拦截器

概述 首先声明一下,这里所说的拦截器是 SpringMVC 的拦截器 HandlerInterceptor.使用SpringMVC 拦截器需要做如下操作: 创建拦截器类需要实现 HandlerInterceptor 在 xml 配置文件中配置该拦截器,具体配置代码如下: <mvc:interceptors> <mvc:interceptor> <!-- /test/** 这个是拦截路径以/test开头的所有的URL--> <mvc:mapping path=&qu

Spring Security整合JWT,实现单点登录,So Easy~!

前面整理过一篇 SpringBoot Security前后端分离,登录退出等返回json数据,也就是用Spring Security,基于SpringBoot2.1.4 RELEASE前后端分离的情况下,实现了登陆登出的功能,亮点就在于以JSON的形式接收返回参数.这个是针对单个后台服务的, 登录信息都存储在SecurityContextHolder缓存里.如果是两个或两个以上的应用呢,那该怎么办?Session是不能用了,Cookie自然也不能用,毕竟它俩是一对的. 曾想过用OAuth2来解决

Spring Security 整合JWT(四)

一.前言 本篇文章将讲述Spring Security 简单整合JWT 处理认证授权 基本环境 spring-boot 2.1.8 mybatis-plus 2.2.0 mysql 数据库 maven项目 Spring Security入门学习可参考之前文章: SpringBoot集成Spring Security入门体验(一)https://blog.csdn.net/qq_38225558/article/details/101754743 Spring Security 自定义登录认证(二

springboot同mybatis整合

springboot和mybatis整合有两种开发模式,首先要做的是配置好开发环境, 实现步骤: 在maven文件pom中配置: 1)SpringBoot同Mybatis整合的依赖. <dependency> <groupId>com.ruijc</groupId> <artifactId>spring-boot-starter-mybatis</artifactId> <version>3.2.2</version> &

springboot+mybatis+springmvc整合实例

以往的ssm框架整合通常有两种形式,一种是xml形式,一种是注解形式,不管是xml还是注解,基本都会有一大堆xml标签配置,其中有很多重复性的.springboot带给我们的恰恰是"零配置","零配置"不等于什么也不配置,只是说相对于传统的ssm框架的xml配置或是注解配置,要少的多.作为常规的来说,一个ssm框架整合,拿maven来说,首先在src/main/resource下加入jdbc.properties,spring-mvc.xml,spring-myba

SpringBoot: 10.整合mybatis(转)

需求:通过使用 SpringBoot+SpringMVC+MyBatis 整合实现一个对数据库中的 t_user 表的 CRUD 的操作 1.创建maven项目,添加项目所需依赖 <!--springboot项目依赖的父项目--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId>

springboot + mybatis + mycat整合

1.mycat服务 搭建mycat服务并启动,windows安装参照. 系列文章: [Mycat 简介] [Mycat 配置文件server.xml] [Mycat 配置文件schema.xml] [Mycat 配置文件rule.xml] 2.相关配置文件 此处我的配置为: schema.xml <?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <myca

springboot与dubbo整合入门(三种方式)

Springboot与Dubbo整合三种方式详解 整合环境: jdk:8.0 dubbo:2.6.2 springboot:2.1.5 项目结构: 1.搭建项目环境: (1)创建父项目与三个子项目,创建项目时,都使用spring initializr,创建时,父项目中注意的一点: (2)创建三个子项目,在已有的父项目上右键,新建模块: (3)创建完成后:将三个子项目在父项目pom.xml中配置: (4)修改所有子项目中的parent标签:(删掉之前parent中的springboot)修改为: