springboot实战学习(7)(JWT令牌的组成、JWT令牌的使用与验证)

news2024/9/23 4:45:58

接着上篇博客的学习。上篇博客是在基本完成用户模块的注册接口的开发以及注册时的参数合法性校验的基础上,基本完成用户模块的登录接口的主逻辑以及提到了问题:"用户未登录,需要通过登录,获取到令牌进行登录认证,才能使用其他功能的接口"。具体往回看了解的链接如下。springboot实战学习(6)(用户模块的登录认证)(初识令牌)(JWT)-CSDN博客文章浏览阅读2次。本篇博客是在处理用户模块中登录认证时遇到需要解决的问题。因为未登录时,需要做到无法访问和使用其他功能的接口。也就提到了令牌的作用以及满足令牌的规范"JWT"。具体的学习下篇博客进行学习...https://blog.csdn.net/m0_74363339/article/details/142365524?spm=1001.2014.3001.5502

接下来就去认真了解和学习Web登录认证中常用的令牌规范——>"JWT"。

目录

一、JWT(令牌规范)

(1)基本介绍

(2)基本组成

(I)解释上方图片(JWT令牌字符串)

(II)总结组成

二、程序中使用JWT令牌 

(1)回顾与思考

三、JWT令牌的生成

(1)JWT令牌示范

(1)如何生成

(2)如何使用生成"JWT令牌"工具

(I)第一步。导入工具的坐标(依赖)

(II)第二步。调用API,生成令牌。

(III)生成"JWT令牌"的代码不需要去记忆

(3)IDEA中写单元测试的方法测试生成JWT令牌

四、JWT令牌的验证

(1)DEA中写单元测试的方法测试"JWT令牌"的验证

(2)测试因篡改"JWT令牌"导致验证失败的几种情况

(I)篡改JWP令牌"头部部分"进行验证。

​编辑

(II)篡改JWP令牌中间"载荷部分"进行验证。

(III)篡改JWP令牌尾部"数字签名"(篡改密钥)进行验证。

(3)处理因篡改JWT令牌的数据导致的异常

(I)如果篡改了头部和载荷部分的数据,那么验证失败。

(II)如果篡改了密钥数据,那么验证失败。

(III)如果超过设定的Token令牌过期时间,那么验证失败。

(4)关于"JWT令牌"验证的注意事项

五、总结

(1)JWT令牌的组成

(2)JWT令牌的使用

(3)尾言


一、JWT(令牌规范)

(1)基本介绍
  • JWT的全称:JSON Web Token
JSON Web Tokens - jwt.ioJSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).icon-default.png?t=O83Ahttps://jwt.io/

也就是用于Web领域的基于JSON格式的令牌

  • 定义了一种简洁的、自包含的格式,用于通信双方以json数据格式安全的传输信息。

(JSON是一种文本数据格式,来源于JavaScript的对象语法。

(2)基本组成

(I)解释上方图片(JWT令牌字符串)
  • 上面展示的它是前后端交互时传输的字符串。而这个字符串是由JSON格式的字符串编码得来的字符串。可以看到上面字符串有两个小".",它就是把这整个字符串分为三个部分。而"分割"的每一个小串,对应着Token令牌的一部分。
  • 第一部分是头。它是由JSON字符串编码得来的。这个部分的字符串会记录两个信息。第一个是"alg"(它是加密算法,防篡改),而"type"(是JWT)。
  • 第二部分是"有效载荷"。它是也是一段字符串。而在这段字符串中,他会存放我们的业务数据。比如存放用户的id、用户的username等等。这段字符串编码后,也会得到上面Token令牌里面的这一段。 

  • 而对应的JSON格式的字符串,是如何转换为上面Token令牌中展示的这一段"特殊长的字符串"呢?在JWT中会借助于"base64"这种编码方式完成。而"base64"可以将任意数据转换成64个可打印字符('a'-'z'、'A'-'Z'、'0'-'9'等等)。这些64个可打印字符最重要的特点就是:"通用",在任意场景都可以被支持。
  • 将JSON格式的字符串转换为64个可打印的字符串,原因是为了提供Token令牌的实用性。记得"base64"仅仅是一种编码方式,而不是加密。因此记得在Token令牌的第二部分"有效载荷",一定注意不要放私密数据(比如用户密码等),不然不安全。


  • 最后一部分叫做"数字签名"。它是将第一部分以及第二部分,借助于密钥和加密算法,通过加密得来的。而这里的加密算法,是通过头部的"alg"来制定的。密钥可以在程序中单独配置。
  • 有了这个数字签名,就可以防篡改,确保Token是安全的。因为将来即使篡改了第一和第二部分,但第三部分是不能篡改的,因为是加密后的字符串。将来JWT再去解析Token令牌时,通过解密第三部分,得到"头部"与"载荷",再拿到解密的内容与用户传递的内容进行比对,如果不一样,就不让访问。
(II)总结组成

二、程序中使用JWT令牌 

(1)回顾与思考
  • 回到之前的所完成用户模块的注册接口与登录接口的主逻辑。

springboot实战学习笔记(5)(用户登录接口的主逻辑)-CSDN博客

  • 现在需要在用户登录之后生成一个"JWT令牌",生成了之后还需要把"JWT令牌"响应给浏览器。
  • 将来浏览器再去访问服务器上的其它资料时,会携带这个令牌访问。这时服务器就能够得到这个令牌,并且还需要去验证这个令牌的合法性。如果令牌合法、没有被篡改,就正常提供服务,反之。
  • 需要学习如何生成令牌?如何验证令牌?

三、JWT令牌的生成

(1)JWT令牌示范

(1)如何生成
  • 可以自己手写,毕竟JWT是一个令牌的规范写法。而是规范,大家都可以实现的。
  • 有人提供了生成"JWT令牌"的工具,所以可以直接使用这些工具即可。
(2)如何使用生成"JWT令牌"工具
(I)第一步。导入工具的坐标(依赖)
  • 引入的是java-jwt

  • 引入坐标时,报错,记得尝试刷新一个Maven

  • 因为我们当前写的"生成令牌"或者"验证令牌"代码,它仅仅是一个测试的代码。所以需要把它写到单元测试里面。所以还要引入单元测试的坐标

  • springboot为了更方便的测试spring程序,提供了springboot整合单元测试的一个起步依赖。

  • 添加完毕,一定记得刷新Maven。

(II)第二步。调用API,生成令牌。
(III)生成"JWT令牌"的代码不需要去记忆
  • 因为将来在公司中使用的时候,都是使用提供好的工具类,然后直接调用即可
(3)IDEA中写单元测试的方法测试生成JWT令牌
  • 在test目录的feisi目录下,新建一个类"JwtTest"。在这个类的内部去写单元测试的代码。

  • 新建一个方法testGen(),并且需要在方法上添加一个注解@Test

  • 如果要生成令牌,就要调用JWT的API。然后先调用其create()方法。
  • 然后利用链式编程的方式来调用方法。首先调用第一个方法"withClaim()",这个方法的作用是添加"载荷"(前面讲过)。

  • 这个添加载荷的方法就是:第一值传入键的名字(name)(如这里设置为"user",那么将来这个载荷就是承载着用户相关的信息),第二个参数值放一个map集合,比如user将来肯定有"id"、"username"等等。
JWT.create()
                .withClaim("user",claims);
  • 所以接着就要创建一个Map类型的集合。指定其键是"String"类型,而值是"Object"类型的
Map<String, Object> claims = new HashMap<>();
  • 有了这个claims集合,就可以调用方法put()。如下添加。这样就提供添加"载荷"的方式,把当前用户信息添加进去了。
claims.put("id",1);
        claims.put("username","张三");
  • 接下来JWT.create().后面还可以添加几个方法。

  • .withExpiresAt()。这是添加过期时间。也就是登录时获得的令牌是有登录有效期的,时间过了,就需要重新登录。将来的Token令牌是有有效期的。
  • 里面是需要一个Data对象。直接new Data()。但是这个是当前的时间,所以还需要通过System提供的获取当前毫秒值的方法,再重新new一个Data对象。往后延迟指定一段时间。
  • 当前毫秒值+1000*60*60*12(也就是延后12个小时)
JWT.create()
                .withClaim("user",claims)
                .withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12));

  • sign()方法。"sign"是签名的意思。也就是如上面说的数字签名,加密用的。方法参数需要指定一个加密算法。这里选择HMAC256()这个算法。
  • 在指定算法的时候,需要指定一个密钥。这个是由自己定就行,就是一个字符串。但是它是加密的密钥,所以不要泄露,不然不能防篡改,保证安全。
.sign(Algorithm.HMAC256("feisi"));//指定算法,配置密钥

  • 这几个方法一旦调用,我们的Token令牌就能生成。所以就要去接收一下Token。
package com.feisi;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.junit.jupiter.api.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;


public class JwtTest {
    //生成JWT令牌的方法
    @Test
    public void testGen(){
        Map<String, Object> claims = new HashMap<>();
        claims.put("id",1);
        claims.put("username","张三");
        //生成JWT代码
        String token = JWT.create()
                .withClaim("user",claims) //添加载荷
                .withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12)) //添加过期时间
                .sign(Algorithm.HMAC256("feisi"));//指定算法,配置密钥
        //打印输出到控制台,查看生成的token令牌
        System.out.println(token);
    }
}

  • 最后运行一下这个测试类的方法。这样"JWT令牌"的生成就完成了。

四、JWT令牌的验证

(1)DEA中写单元测试的方法测试"JWT令牌"的验证
  • 首先这一部分的代码也不需要去背或者记忆。
  • 接着上面,在"testGen()"方法下添加一个方法"testParse()"。
  • 也是一样记得添加单元测试的注解@Test


  • 接着定义一个字符串"token",模拟存储用户传递给浏览器的token令牌。
  • 而这个token的值就用上面控制台打印的token令牌字符串就行。
//验证JWT令牌的方法
    @Test
    public void testParse(){
        //定义字符串,模拟用户传递给浏览器的token令牌
        //这个token就用上面生成的token令牌字符串就行
        String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
                +".eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE3MjcwMzg2MDl9"
                +".LgfVNY-OAkjAps8yQXDkKyCIRbWw5jNQiNZwwbMf2F4";
    }

  • 接着开始提供验证了。去提供调用JWT这个类里面的一个静态方法"require()"。这是申请一个JWT的验证器。
  • 而这个方法里面的参数需要传递一个算法。之前加密用的算法,解密也要同样用一样的算法。直接复制过来。
  • 然后再调用一个".build()"方法去生成验证器。再用一个变量"jwtVerifier"给他存起来。
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("feisi")).build();

  • 有了这个变量之后,就可以调用验证器的方法,去验证这个token令牌字符串。
  • 调用方法".verify()",把之前控制台的token传递给它。


  • 它可以去解析这个token令牌。然后生成一个解析后的JWT对象。
DecodedJWT decodedJWT = jwtVerifier.verify(token); //验证token,生成一个解析后的JWT对象
  • 如果能正常的解析成功之后。那么就意味着可以从变量"decodedJWT"里面获取到当前的"头部"或者"载荷"以及"数字签名"等等。
  • 则调用它的一个方法:"GetClaims()",而它会得到所有的"载荷"。用Map对象的变量去接收一下它。
Map<String, Claim> claims = decodedJWT.getClaims();
  • 然后我们指定获取键名(name)为"user"的载荷。并且把得到的结果输出到控制台里面。
package com.feisi;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.jupiter.api.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;


public class JwtTest {

    //生成JWT令牌的方法
    @Test
    public void testGen(){
        Map<String, Object> claims = new HashMap<>();
        claims.put("id",1);
        claims.put("username","张三");
        //生成JWT代码
        String token = JWT.create()
                .withClaim("user",claims) //添加载荷
                .withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12)) //添加过期时间
                .sign(Algorithm.HMAC256("feisi"));//指定算法,配置密钥
        //打印输出到控制台,查看生成的token令牌
        System.out.println(token);
    }
    //验证JWT令牌的方法
    @Test
    public void testParse(){
        //定义字符串,模拟用户传递给浏览器的token令牌
        //这个token就用上面生成的token令牌字符串就行
        String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
                +".eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE3MjcwMzg2MDl9"
                +".LgfVNY-OAkjAps8yQXDkKyCIRbWw5jNQiNZwwbMf2F4";
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("feisi")).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token); //验证token,生成一个解析后的JWT对象
        Map<String, Claim> claims = decodedJWT.getClaims();
        System.out.println(claims.get("user"));

    }
}

  • 得到控制台的正常输出,如:"id,1"、"username,"张三""等等就解析成功了!
  • 正常的解析成功如下。


(2)测试因篡改"JWT令牌"导致验证失败的几种情况
(I)篡改JWP令牌"头部部分"进行验证。

(II)篡改JWP令牌中间"载荷部分"进行验证。

  • 所以我们的JWT令牌是具体放篡改的功能的。

(III)篡改JWP令牌尾部"数字签名"(篡改密钥)进行验证。
  • 篡改了原来设定的密钥"feisi"改成"fesi"。


(3)处理因篡改JWT令牌的数据导致的异常
(I)如果篡改了头部和载荷部分的数据,那么验证失败。
(II)如果篡改了密钥数据,那么验证失败。
(III)如果超过设定的Token令牌过期时间,那么验证失败。

Token过期

  • 例如如下情况:报错提示"这个Token令牌"已经过期。

(4)关于"JWT令牌"验证的注意事项

五、总结

(1)JWT令牌的组成

  • "载荷"这一块要注意。不要存放一些私密信息,因为"base64"算法不是一个加密算法,它是公开的,每个人都能使用
  • 通过密钥与加密算法去验证Token令牌的合法性
(2)JWT令牌的使用

(借助工具:"JAVA-JWT")

(3)尾言

下篇博客就要开始继续完成用户登录认证的接口开发了!也就是把令牌的生成与验证加入到登录功能中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2156558.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity对象池的高级写法 (Plus优化版)

唐老师关于对物体分类的OOD的写法确实十分好&#xff0c;代码也耦合度也低&#xff0c;但是我有个简单的写法同样能实现一样的效果&#xff0c;所以我就充分发挥了一下主观能动性 相较于基本功能&#xff0c;这一版做出了如下改动 1.限制了对象池最大数量&#xff0c;多出来的…

Pybullet 安装过程

Pybullet 安装过程&#xff08;windows&#xff09; 1. 安装C编译工具2. 安装Pybullet 1. 安装C编译工具 pybullet 需要C编译套件&#xff0c;直接装之前检查下&#xff0c;要不会报缺少某版本MVSC的error&#xff0c;最好的方式是直接下载visual studio&#xff0c;直接按默认…

多无人机通信(多机通信)+配置ssh服务

目录 多机通信 设备 主从机通信设置 配置从机 配置主机 测试 正式启用 MAVROS通信 多机通信 多机通信是实现机器人编队的基础&#xff0c;通过网络搭建通信链路。我们这里用中心节点网络通信&#xff0c;所有数据需有经过中心节点&#xff0c;所以&#xff0c;中心节点…

【有啥问啥】探索累计推理(Cumulative Reasoning, CR)——大型语言模型中的复杂推理新框架

探索累计推理&#xff08;Cumulative Reasoning, CR&#xff09;——大型语言模型中的复杂推理新框架 引言 随着人工智能&#xff08;AI&#xff09;的快速发展&#xff0c;大型语言模型&#xff08;LLMs&#xff09;在自然语言处理上的表现令人瞩目。然而&#xff0c;LLMs在…

实现人体模型可点击

简化需求&#xff1a;实现项目内嵌人体模型&#xff0c;实现点击不同部位弹出部位名称 一&#xff1a;优先3d&#xff0c; 方案&#xff1a;基于three.js&#xff0c;.gltf格式模型&#xff0c;vue3 缺点&#xff1a;合适且免费的3d模型找不到&#xff0c;因为项目对部位有要…

深度学习——D2(数据操作)

N维数组 创建数组 访问元素 一列: [ : , 1 ] 反向累积、正向累积&#xff08;自动求导&#xff09; 梯度 梯度&#xff08;Gradient&#xff09;是微积分中的一个重要概念&#xff0c;主要用于描述一个函数在某个区域内的变化情况。以下是对梯度的详细解释&#xff1a; 一…

树莓派pico上手

0 介绍 不同于作为单板计算机的树莓派5&#xff0c;树莓派 pico 是一款低成本、高性能的微控制器板&#xff0c;具有灵活的数字接口。主要功能包括&#xff1a; 英国树莓派公司设计的 RP2040 微控制器芯片双核 Arm Cortex M0 处理器&#xff0c;弹性的时钟频率高达 133 MHz26…

Qt笔记(十七)cmake编译Qt项目

Qt笔记&#xff08;十七&#xff09;cmake编译Qt项目 1. 文件内容与文件结构1.1.文件目录1.2. CMakeLists.txt内容1.3. main.cpp文件1.4. mouseevent.h1.5. mouseevent.cpp1.6. 生成Visual Studio项目后编译报错1.7. 界面显示中文乱码问题 1. 文件内容与文件结构 1.1.文件目录…

神奇的可变模板参数的应用(C++标准库双向链表 list 中的emplace函数实现)

我们先来看一个可以构造任意对象的函数&#xff1a; /// <summary> /// 可以构造任意对象的函数 /// </summary> /// <typeparam name"MyClass">要转换对象的类型</typeparam> /// <typeparam name"...MyClassConstructorParameterT…

传输层 II(TCP协议——协议的特点、报文段、连接管理)【★★★★】

&#xff08;★★&#xff09;代表非常重要的知识点&#xff0c;&#xff08;★&#xff09;代表重要的知识点。 一、TCP 协议的特点 TCP 是在不可靠的 IP 层之上实现的可靠的数据传输协议&#xff0c;它主要解决传输的可靠、有序、无丢失和不重复问题。TCP 是 TCP/IP 体系中非…

使用Stream实现事件流

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了Flutter中的异步操作&#xff0c;本章回中将介绍Flutter中的事件流.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在上一章回中介绍了异步操作相关的内容&#xff0c;本章回中将介绍如何把…

施耐德EcoStruxure Machine SCADA Expert(EMSE)与SQL数据库连接(十五)

我习惯使用SQL Server 数据库与EMSE进行连接。 用的是sql 2017 关于数据库软件的安装教程 网上一大把。 1.新建数据库 打开数据库管理工具&#xff0c;新建数据库 2.新建表单 &#xff08;ps:这里先做一个小测试-----目的是验证与EMSE软件的链接是否顺畅。) 添加两个元素进去…

图神经网络的新篇章:通用、强大、可扩展的图变换器

人工智能咨询培训老师叶梓 转载标明出处 图变换器&#xff08;Graph Transformers, GTs&#xff09;因其在处理节点间全局依赖关系方面的能力而受到广泛关注。然而&#xff0c;现有的GTs模型在处理大规模图时面临着计算复杂度高、泛化能力有限等问题。为了解决这些问题&#x…

对比评测5款实用在线翻译工具,包括有道在线翻译

大家好&#xff0c;今天咱们来聊聊在线翻译工具。在这个信息爆炸的时代&#xff0c;语言不再是沟通的障碍&#xff0c;多亏了这些强大的翻译神器。今天&#xff0c;我将带大家比较五款热门的在线翻译工具&#xff0c;究竟谁更胜一筹呢&#xff1f;让我们一探究竟&#xff01; …

用友U8CRM relobjreportlist.php SQL注入漏洞复现

0x01 漏洞描述&#xff1a; 用友U8 CRM客户关系管理系统是一款专业的企业级CRM软件&#xff0c;旨在帮助企业高效管理客户关系、提升销售业绩和提供优质的客户服务。 用友 U8 CRM客户关系管理系统relobjreportlist.php 文件存在SQL注入漏洞&#xff0c;未经身份验证的攻击者通过…

Linux 一些快捷键使用操作技巧

ctrl c : 强制停止 如图仅输入tail命令时程序会卡住&#xff0c;这时就需要强制停止 ctrl d : 退出或者登出 history : 查看历史输入命令 &#xff01;命令 &#xff1a;自动执行上一次匹配前缀的命令 &#xff08;注意不要用这个命令执行太过久远的&#xff0c;容易执行错误…

字节数据转16进制对应十进制数

在数据处理中经常面临字节数据需要转换成不同位宽的十进制数据&#xff0c;尤其是在嵌入式处理中该现象特别常见&#xff0c;这里以转换为16位位宽的十进制为例&#xff0c;采用python校本进行数据转换&#xff0c;具体数据如下&#xff1a; 要将上面数据转换为双字节十进制数…

英语六级-学习

01 英语分值比例 02听力学习 听力练习&#xff0c;基础好选择标准VOA和BBC。基础差选择VOA慢速。 听力内容包括不受政治争议的内容&#xff0c;社会生活类(奇闻趣事、日常生活)、经济类(商务、职场相关)、环保类、互联网类---------根据各类主题快速找到录音材料中心点。 研…

文心一言 VS 讯飞星火 VS chatgpt (352)-- 算法导论24.1 3题

三、给定 G(V,E) 是一带权重且没有权重为负值的环路的有向图&#xff0c;对于所有结点 v∈V ,从源结点 s 到结点 v 之间的最短路径中&#xff0c;包含边的条数的最大值为 m 。&#xff08;这里&#xff0c;判断最短路径的根据是权重&#xff0c;不是边的条数。&#xff09;请对…

leetcode:最高乘法得分

用auto可以过 class Solution { public:long long maxScore(vector<int>& a, vector<int>& b) {int n b.size();vector<vector<long long>> memo(4,vector<long long>(b.size(), LLONG_MIN));auto dfs [&](auto&& dfs, i…