JWT介绍、测试案例 以及实际开发中的使用
什么是JWT?
JWT,通过数字签名的方式,以json对象为载体,在不同的服务终端之间安全的传输信息,用来解决传统session的弊端。
JWT在前后端分离系统,通过JSON形式作为WEB应用中的令牌(token),用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中,还可以完成数据加密、签名等相关处理。
JWT能做什么?
1.授权:一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单点登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。
2.信息交换:jwt是在各方之间安全地传输信息的好方法,因为可以对JWT进行签名,所以可以确保发件人是他们所说的人,此外可以验证内容是否遭到篡改。
为什么会有JWT?
传统的session认证有如下的问题
1.每个用户经过我们的应用认证之后,将认证信息保存在session中,由于session服务器中对象,随着认证用户的增多,服务器内存开销会明显增大;
2.用户认证之后,服务端使用session保存认证信息,那么要取到认证信息,只能访问同一台服务器,才能拿到授权的资源。这样在分布式应用上,就需要实现session共享机制,不方便集群应用;
3.因为session是基于cookie来进行用户识别的,cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
基于JWT的认证流程:
- 前端通过web表单将自己的用户和密码发送到后端接口(一般是http-post请求,建议使用SSL加密传输(https协议),以免敏感信息被嗅探)
- 后端核对用户名和密码成功后,将用户的Id等其他信息作为JWT Payload(负载),将其与头部分别进行BASE64编码拼接后签名,形成一个JWT(Token),形成的JWT就是一个字符(head.payload.singueater)
- 后端将JWT字符串作为登录成功的结果返回给前端。前端结果保存在localStorage(本地缓存)或sessionStorage上,退出登录时前端删除保存的JWT即可。
- 前端在每次请求时将JWT放入http header中的Authorization位(解决XSS和XSRF问题)
- 后端检查是否存在,如存在验证JWT的有效性,例如:检查签名是否正确,检查token是否过期,检查token的接收方是否是自己(可选)。
JWT的优势:
- 简洁,可以通过URL、POST参数或Http header发送,因为数据量小,传输速度快;
- 自包含,负载(属于JWT的一部分)中包含了用户所需要的信息,不需要在服务器端保存会话信息,不占服务器内存,也避免了多次查询数据库,特别适用于分布式微服务;
- 因为token是以json加密的形式保存在客户端的,所以JWT可以跨语言使用,原则上任何WEB形式都支持。
- 不需要再服务端保存会话信息,特别适用于分布式微服务。
JWT结构
JWT其实就是一段字符串,由标头(Header)、有效载荷(Payload)和签名(Signature)这三部分组成,用 . 拼接。在传输的时候,会将JWT的三部分分别进行Base64编码后用 . 进行连接形成最终传输的字符串。
头部(Header): JWT的头部是一个JSON对象,用于描述JWT的元数据,例如令牌的类型(typ)和签名算法(alg)。通常情况下,头部会包含以下信息:
{"alg": "HS256","typ": "JWT"
}
- alg:指定签名算法,常见的有HMAC SHA256(HS256)和RSA SHA256(RS256)等。
- typ:指定令牌的类型,一般为JWT。
头部需要经过Base64编码后作为JWT的第一部分。
载荷(Payload):JWT的载荷是存储实际数据的部分,也是一个JSON对象。它包含了一些声明(claims),用于描述令牌的信息。常见的声明有:
{"sub": "1234567890","name": "John Doe","admin": true
}
前面两部分都使用Base64进行编码,前端可以解开知道里面的信息, Signature需要使用编码后的header和payload以及我们提供的一密钥,然后使用header中指定的签名算法进行签名,以保证JWT没有被篡改过。
使用Signature签名可以防止内容被篡改。如果有人对头部及负载内容解码后进行修改,再进行编码,最后加上之前签名组成新的JWT。那么服务器会判断出新的头部和负载形成的签名和JWT附带的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。
当用户希望访问一个受保护的路由或者资源的时候,可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求头信息的 Authorization 字段里,使用 Bearer 模式添加 JWT。
JWT测试demo
package com.qcby.springbootdemo1;
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的生成:@Testpublic void testGen(){Map<String,Object> claims = new HashMap<>();claims.put("id",1);claims.put("username","张三");String token = JWT.create().withClaim("user",claims) //t添加第二部分.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12)) //添加过期时间.sign(Algorithm.HMAC256("itheima"));//指定算法,配置 密钥System.out.println(token);}//JWT的验证:@Testpublic void testGen1(){//定义字符串,模拟前端传过来的tokenString token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE3MzE2MjE4NjZ9.XKk_AiJ5njz7tXRG9xW5lWdrvq71LzIhiHKrvYsvvbI";//require:申请一个jwt的验证器JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("itheima")).build();DecodedJWT decodedJWT = jwtVerifier.verify(token);//验证token,生成一个解析后的JWT对象Map<String, Claim> claims = decodedJWT.getClaims();System.out.println(claims.get("user"));//拿到token中的信息//如果篡改了头部和载荷部分的数据,那么验证失败//如果密钥改了,验证失败//token过期}
}
封装的JwtUtil:
package com.qcby.gaokao.util;
/*** JWT工具类*/
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;public class JwtUtil {//有效期为public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 * 1000//设置秘钥明文public static final String JWT_KEY = "qcby";/*** 创建token* @param id* @param subject* @param ttlMillis* @return*/public static String createJWT(String id, String subject, Long ttlMillis) {//设置签名算法:SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;//获取当前时间,为设置过期时间准备long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);//如果调用方法时没有定义密钥,就是JwtUtil中定义的if(ttlMillis==null){ttlMillis=JwtUtil.JWT_TTL;}//设置过期时间long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);//设置加密后的密钥SecretKey secretKey = generalKey();JwtBuilder builder = Jwts.builder().setId(id) //唯一的ID.setSubject(subject) // 主题 可以是JSON数据.setIssuer("wd") // 签发者.setIssuedAt(now) // 签发时间.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥.setExpiration(expDate);// 设置过期时间return builder.compact();}/*** 生成加密后的秘钥 secretKey* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 解析* @param jwt* @return* @throws Exception*/public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}public static void main(String[] args) {String token = JwtUtil.createJWT(UUID.randomUUID().toString(),"qd",null );System.out.println(token);}
}
在项目中的应用:
我是在登录功能的时候使用的JWT技术
- 在我第一次登录的时候就会生成token令牌
- 我的令牌的校验的功能在拦截器中进行了实现
登录部分:
List<Student> students = studentMapper.findone(student);
if(students.size()== 1){//这些就是在进行登录拦截String token = JwtUtil.createJWT(UUID.randomUUID().toString(),String.valueOf(students.get(0).getId()),null);//将token信息设置如cookie当中Cookie cookie = new Cookie("token",token);cookie.setPath("/"); //设置浏览器的访问路径cookie.setMaxAge(36000); //设置cookie的过期时间response.addCookie(cookie);/*** 下面是用于传递账户邮箱信息的功能*///1、获取该账户的邮箱String email = students.get(0).getEmail();//2、将邮箱信息放入CookieCookie cookie1 = new Cookie("email",email);cookie1.setPath("/");cookie1.setMaxAge(36000);response.addCookie(cookie1);//到此逻辑结束//登录成功return new ResponseResult(200,"登录成功");}else {//登录失败return new ResponseResult(444,"登录失败");}
拦截器部分:
/*
* 配置拦截器
* */
@Component
public class LoginInterceptor implements HandlerInterceptor {//拦截器@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Cookie[] cookies = request.getCookies();if(cookies != null){for (Cookie cookie:cookies){if("token".equals(cookie.getName())){String userToken = cookie.getValue();if(!StringUtils.hasText(userToken)){response.sendError(HttpServletResponse.SC_UNAUTHORIZED);}//解析token看看是否成功try {Claims claims = JwtUtil.parseJWT(userToken);claims.getSubject();}catch (Exception e){//e.printStackTrace();System.out.println("token信息出错");return false;}return true; //放行}}}return false;}
}
相关文章:

JWT介绍、测试案例 以及实际开发中的使用
什么是JWT? JWT,通过数字签名的方式,以json对象为载体,在不同的服务终端之间安全的传输信息,用来解决传统session的弊端。 JWT在前后端分离系统,通过JSON形式作为WEB应用中的令牌(token),用于…...

快排和归并
目录 前言 快速排序 相遇位置一定比key小的原理(大): 避免效率降低方法(快排优化) 三数取中(选key优化) 小区间优化 hoare版本快排 挖坑法快排 前后指针快排 非递归快排 归并排序 非递…...

VUE+SPRINGBOOT实现邮箱注册、重置密码、登录功能
随着互联网的发展,网站用户的管理、触达、消息通知成为一个网站设计是否合理的重要标志。目前主流互联网公司都支持手机验证码注册、登录。但是手机短信作为服务端网站是需要付出运营商通信成本的,而邮箱的注册、登录、重置密码,无疑成为了这…...

Vue 项目打包后环境变量丢失问题(清除缓存),区分.env和.env.*文件
Vue 项目打包后环境变量丢失问题(清除缓存),区分.env和.env.*文件 问题背景 今天在导报项目的时候遇到一个问题问题:在开发环境中一切正常,但在打包后的生产环境中,某些环境变量(如 VUE_APP_B…...

创建vue+electron项目流程
一个vue3和electron最基本的环境搭建步骤如下:// 安装 vite vue3 vite-plugin-vue-setup-extend less normalize.css mitt pinia vue-router npm create vuelatest npm i vite-plugin-vue-setup-extend -D npm i less -D npm i normalize.css -S ࿰…...
3. 用Ruby on Rails创建一个在线商城
哎呀,你这是想要我写一篇超长篇的Ruby on Rails教程啊!好吧,既然你这么热情,那我就勉为其难地给你来一篇生动有趣、充满比喻夸张讽刺修辞手法的教程吧! 1. 准备工作 1.1. 安装Ruby和Rails 1.1.1 安装Ruby 下载Ruby…...

jmeter常用配置元件介绍总结之配置元件
系列文章目录 1.windows、linux安装jmeter及设置中文显示 2.jmeter常用配置元件介绍总结之安装插件 3.jmeter常用配置元件介绍总结之线程组 4.jmeter常用配置元件介绍总结之函数助手 5.jmeter常用配置元件介绍总结之取样器 6.jmeter常用配置元件介绍总结之jsr223执行pytho…...
SpringBoot获取请求参数
spring boot获取请求参数 文章目录 spring boot获取请求参数一、简单参数二、实体参数三、数组集合参数四、日期参数五、Json参数六、路径参数 开头概述 在Spring Boot框架中,处理HTTP请求并获取请求参数是开发Web应用程序中的一项基本任务。无论是简单的GET请求还是…...

【数据结构】树——顺序存储二叉树
写在前面 在学习数据结构前,我们早就听说大名鼎鼎的树,例如什么什么手撕红黑树大佬呀,那这篇笔记不才就深入浅出的介绍二叉树。 文章目录 写在前面一、树的概念及结构1.1、数的相关概念1.2、数的表示1.3 树在实际中的运用(表示文…...
Android中perform和handle方法的区别——以handleLaunchActivity与performLaunchActivity为例
在Android系统中,perform和handle方法经常出现在关键流程中,分别承担不同的职责。这种命名约定反映了框架设计中的分层思想,帮助开发者区分任务的调度与实现。本文通过handleLaunchActivity和performLaunchActivity这两个典型方法的源码分析&…...

聊聊依赖性测试
在软件测试中,我们常常面临一个挑战:多个模块之间高度耦合,任何一个模块的异常都可能导致整个系统崩溃。如何确保这些模块之间的协作无缝衔接?这就需要依赖性测试的助力! 什么是依赖性测试?它与功能测试、…...

C++11————线程库
thread 类的简单介绍 在 c11 之前,涉及到多线程问题,都是和平台相关的,比如 windows 和 linux 下各自有自己的接口,这使得代码的可移植性比较差。在 c11 中引入了线程库,使得 c在编程时不需要依赖第三方库了 函数名 …...
Java 动态代理初步
动态代理初步 package ReflectExercise;import ReflectExercise.pojo.BigStar; import ReflectExercise.pojo.ProxyUtil; import ReflectExercise.pojo.Star;/*** 动态代理* 无侵入的给方法增强功能*/ public class ReflectExercise {public static void main(String[] args) {…...

应用系统开发(10) 钢轨缺陷的检测系统
涡流检测系统框图 其中信号发生器为一定频率的正弦信号作为激励信号,这个激励信号同时输入给交流电桥中的两个检测线圈,将两个线圈输出的电压差值作为差分信号引出至差分放大电路进行放大,经过放大后信号变为低频的缺陷信号叠加在高频载波上…...
理解 \r、\n、\r\n 和 \n\r:换行符的区别和用法
\r(回车,Carriage Return): ASCII 码 13,对应的控制字符是 CR,将光标回到当前行的行首(而不会换到下一行),之后的输出会把之前的输出覆盖。\n(换行,Line Feed)…...
【jvm】StringTable为什么要调整
目录 1. 永久代内存限制与回收效率2. 堆内存的优势3. JDK版本的演进4. 实际应用的考虑 1. 永久代内存限制与回收效率 1.内存限制:在JDK 6及之前的版本中,StringTable位于永久代(PermGen space)中。然而,永久代的内存空…...
AI 驱动低代码平台:开创智能化用户体验新纪元
一、引言 人工智能技术如汹涌浪潮般迅猛发展,在各个行业掀起了颠覆性的变革风暴。于软件开发领域而言,AI 辅助编程与低代码平台的完美结合已然成为关键趋势,极大地提高了开发效率。然而,低代码平台的使命绝非仅仅局限于简化开发流…...

谈一谈QThread::CurrentThread和this->thread
QThread::CurrentThread是指的当前函数调用者者所在的线程 this->thread是指的当前对象所在的线程(对象创建出来的时候所在的线程) Qt文档说明 CurrentThread返回一个指向管理当前执行线程的QThread的指针 thread返回对象所在的线程 这两个函数所…...

ThriveX 博客管理系统前后端项目部署教程
前端 前端项目地址:https://github.com/LiuYuYang01/ThriveX-Blog 控制端项目地址:https://github.com/LiuYuYang01/ThriveX-Admin Vercel 首先以 Vercel 进行部署,两种方式部署都是一样的,我们以前端项目进行演示 首先我们先…...

STM32单片机设计防儿童人员误锁/滞留车内警报系统
目录 目录 前言 一、本设计主要实现哪些很“开门”功能? 二、电路设计原理图 1.电路图采用Altium Designer进行设计: 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 近年来在车辆逐渐普及的情况下,由于家长的疏忽,将…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...

认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...