JSON Web Token (JWT): 理解与应用

JWT(JSON Web Token)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。JWT通常用于身份验证和授权目的,因为它可以使用JSON对象在各方之间安全地传输信息

官网地址:https://jwt.io/

0.介绍

通俗地说,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。

0.1.JWT的应用场景

身份验证 : 当用户成功登录时,服务器会生成一个JWT并将其发送给客户端。客户端在后续请求中将JWT附加到HTTP请求头中,以此来证明用户的身份。

授权 : JWT中可以包含用户的权限信息,这样服务器可以根据这些信息决定用户是否被允许访问某些资源。

信息传递 : 除了用户的身份和权限外,JWT还可以用来携带其他有用的信息,如用户的偏好设置等。

在 java 中 常与 Spring Security 框架配合使用

JWT的认证流程如下:

  1. 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探
  2. 后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
  3. 后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可
  4. 前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)
  5. 后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等
  6. 验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

在这里插入图片描述

0.2.优点

这种基于token的认证方式相比传统的session认证方式更节约服务器资源,并且对移动端和分布式更加友好。其优点如下:

  • 支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题

  • 无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力

  • 更适用CDN:可以通过内容分发网络请求服务端的所有资料

  • 更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多

  • 无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御

1.JWT的结构

JWT是一种自包含的令牌格式。JWT由三个部分组成:头部、载荷和签名。

在这里插入图片描述

1.1.头部 (Header)

头部通常包含两个部分:

  • typ: 表示该令牌的类型,通常是“JWT”。
  • alg: 指定签名算法,例如 HMAC SHA-256 或 RSA。

头部通常以JSON格式书写,并经过Base64Url编码。

1.2.载荷 (Payload)

载荷包含了需要作为声明传输的信息。这些声明可以分为三类:

  • 标准声明:由JWT规范定义的声明。
    • iss (issuer): 发行者。
    • sub (subject): 主题,通常是指用户ID。
    • aud (audience): 接收者,即令牌的预期受众。
    • exp (expiration time): 过期时间。
    • nbf (not before): 该时间之前不可使用。
    • iat (issued at): 发行时间。
    • jti (JWT ID): 一个唯一的标识符,用于防止重放攻击。
  • 私有声明:由发行者和接收者约定的声明,例如用户的角色或权限等。
  • 公共声明:虽然不是JWT规范的一部分,但可以在任何JWT中使用。

载荷也是经过Base64Url编码的。

1.3.签名 (Signature)

签名部分保证了JWT的完整性和安全性。签名通过将头部和载荷进行编码并使用指定的算法(如HMAC SHA-256 RSA或ECDSA)进行计算得到。签名确保了:

  • 令牌没有被篡改。
  • 令牌是由可信的一方发行的。

签名部分同样经过Base64Url编码。

1.4.JWS, JWK

JWS ,也就是JWT Signature,其结构就是在之前nonsecure JWT的基础上,在头部声明签名算法,并在最后添加上签名。创建签名,是保证jwt不能被他人随意篡改。我们通常使用的JWT一般都是JWS

为了完成签名,除了用到header信息和payload信息外,还需要算法的密钥,也就是secretKey。

加密的算法一般有2类:

  • 对称加密:secretKey指加密密钥,可以生成签名与验签
  • 非对称加密:secretKey指私钥,只用来生成签名,不能用来验签(验签用的是公钥)

JWT的密钥或者密钥对,一般统一称为JSON Web Key,也就是JWK

到目前为止,jwt的签名算法有三种:

  • HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
  • RSASSA【RSA签名算法(非对称)】 :(RS256/RS384/RS512)
  • ECDSA【椭圆曲线数据签名算法(非对称)】 :(ES256/ES384/ES512)

2.实现与工具

2.1.jjwt (Java JWT)

GitHub 仓库地址:https://github.com/jwtk/jjwt

这是一个基于Java的库,用于在JVM和Android平台上创建和验证JSON Web Tokens (JWTs)和JSON Web Keys (JWKs)。

它支持多种JOSE工作组的RFC规范:

  • RFC 7519: JSON Web Token (JWT)
  • RFC 7515: JSON Web Signature (JWS)
  • RFC 7516: JSON Web Encryption (JWE)
  • 等等
2.1.1.导入Maven依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version>
</dependency>
2.1.2.测试代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.security.Key;
import java.util.Date;
import java.util.UUID;@RestController
public class JwtController {// 密钥 : 实际开发时 应该从 配置文件 / 持久化存储中 获取private String secret;// token时效:24小时public static final long EXPIRE = 1000 * 60 * 60 * 24;@RequestMapping("/create")public String createToken(){// 生成一个随机的密钥 IDString keyId = UUID.randomUUID().toString();// 密钥secret = keyId;// 创建一个 JWT 令牌String jwt = Jwts.builder()// 设置JWT头部参数,指定令牌类型为JWT.setHeaderParam("typ", "JWT")// 设置JWT头部参数,指定签名算法为HS256.setHeaderParam("alg", "HS256")// 设置JWT过期时间,当前时间戳加上设定的过期时长.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))// 设置主题,通常是用户身份标识.setSubject("admin")// 添加角色声明.claim("role", "admin")// 设置用户ID为1.claim("id", 1)// 设置用户昵称为"王小二".claim("nickname", "王小二")// 设置令牌签发时间.setIssuedAt(new Date())// 设置唯一标识符.setId( keyId )// 使用HS256算法和密钥签名.signWith(SignatureAlgorithm.HS256, secret.getBytes())// 将令牌压缩为紧凑形式的字符串.compact();return jwt;}@RequestMapping("/check")public  void checkToken(String jwt) {System.out.println("jwt = " + jwt);// 创建一个安全的密钥Key secureKey = Keys.hmacShaKeyFor(secret.getBytes());try {// 使用Jwts.parserBuilder()方法构建一个解析器,该解析器使用secureKey作为签名密钥// 然后使用这个解析器解析jwt字符串,获取到Claims对象,即JWT的主体部分Claims claims = Jwts.parserBuilder().setSigningKey(secureKey).build().parseClaimsJws(jwt).getBody();// 打印解析后的 JWT 信息System.out.println("Subject: " + claims.getSubject());System.out.println("Role: " + claims.get("role", String.class));System.out.println("Issue Time: " + claims.getIssuedAt());System.out.println("JWT ID: " + claims.getId());System.out.println("user nickname: " + claims.get("nickname"));// 可以在这里添加更多逻辑来验证 JWT 的有效性,例如检查过期时间等if(claims.getExpiration()==null){System.out.println("过期时间不能为空 Expiration time cannot be null");}} catch (Exception e) {// 如果 JWT 无法验证,这里会捕获异常System.err.println("Invalid JWT: " + e.getMessage());}}
}

2.2.Nimbus

Nimbus JOSE + JWT 是一个非常强大的 Java 库,用于处理 JSON Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), JSON Web Token (JWT) 和 OAuth 2.0 授权服务器。这个库是由 NimbusDS 开发的,并且广泛应用于身份验证和授权系统中。

2.2.1.导入依赖

首先,您需要在 Maven 项目中添加 json-jwt 的依赖:

        <dependency><groupId>com.nimbusds</groupId><artifactId>nimbus-jose-jwt</artifactId><version>9.40</version></dependency>
2.2.2.测试代码

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.text.ParseException;
import java.util.Date;
import java.util.UUID;@RestController
public class JwtController {// 密钥 : 实际开发时 应该从 配置文件 / 持久化存储中 获取private String secret;@RequestMapping("/create")public String createToken() {try {// 生成一个随机的密钥 IDString keyId = UUID.randomUUID().toString();// 密钥secret = keyId;// 创建一个 HMAC 签名器MACSigner signer = new MACSigner(secret.getBytes());// 设置 JWT 的声明JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();builder.subject("admin");builder.claim("role", "admin");builder.issueTime(new Date());builder.jwtID(keyId);// 构建 JWT Claims SetJWTClaimsSet claimsSet = builder.build();// 创建一个空的 SignedJWT 对象SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256),claimsSet);// 签名 JWTsignedJWT.sign(signer);// 将 JWT 转换为紧凑形式String jwt = signedJWT.serialize();return jwt;} catch (JOSEException e) {e.printStackTrace();}return null;}@RequestMapping("/check")public void checkToken(String jwt) {try {System.out.println("jwt = " + jwt);// 解析 JWTSignedJWT signedJWT = SignedJWT.parse(jwt);// 验证 JWTMACVerifier verifier = new MACVerifier( secret.getBytes() );boolean isValid = signedJWT.verify(verifier);if (!isValid) {System.out.println("无效的 JWT 签名 Invalid JWT signature.");return;}// 获取 JWT ClaimsJWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();System.out.println("Subject: " + claimsSet.getSubject());System.out.println("Role: " + claimsSet.getStringClaim("role"));System.out.println("Issue Time: " + claimsSet.getIssueTime());System.out.println("JWT ID: " + claimsSet.getJWTID());} catch (JOSEException | ParseException e) {e.printStackTrace();}}/*** token 通过 header传递* 再通过 request 取出*/@RequestMapping("/checkHeader")public void checkHeaderToken(HttpServletRequest request) {try {String jwt = request.getHeader("Authorization");System.out.println("jwt header=> " + jwt);// 解析 JWTSignedJWT signedJWT = SignedJWT.parse(jwt);// 验证 JWTMACVerifier verifier = new MACVerifier( secret.getBytes() );boolean isValid = signedJWT.verify(verifier);if (!isValid) {System.out.println("无效的 JWT 签名 Invalid JWT signature.");return;}// 获取 JWT ClaimsJWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();System.out.println("Subject: " + claimsSet.getSubject());System.out.println("Role: " + claimsSet.getStringClaim("role"));System.out.println("Issue Time: " + claimsSet.getIssueTime());System.out.println("JWT ID: " + claimsSet.getJWTID());} catch (JOSEException | ParseException e) {e.printStackTrace();}}
}
2.2.3.请求测试
###
GET http://localhost:8080/create

生成token : eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyMzQ1MDgyOCwiaWF0IjoxNzIzMzY0NDI4LCJqdGkiOiI5Yzc3N2YwZC03NDU5LTQ3MTUtYmVkNy1mNWViYzJiNmMwOTgifQ.Y_P7L4gehl0kJwxTnwUxX8Yy502qrCHQ0hkxod58ly8

###
GET http://localhost:8080/check?jwt=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyMzQ1MDgyOCwiaWF0IjoxNzIzMzY0NDI4LCJqdGkiOiI5Yzc3N2YwZC03NDU5LTQ3MTUtYmVkNy1mNWViYzJiNmMwOTgifQ.Y_P7L4gehl0kJwxTnwUxX8Yy502qrCHQ0hkxod58ly8

idea 控制台:
Subject: admin
Role: admin
Issue Time: Sun Aug 11 16:20:28 CST 2024
JWT ID: 9c777f0d-7459-4715-bed7-f5ebc2b6c098

###
GET http://localhost:8080/checkHeader
Content-Type: application/json
Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyMzQ1MDgyOCwiaWF0IjoxNzIzMzY0NDI4LCJqdGkiOiI5Yzc3N2YwZC03NDU5LTQ3MTUtYmVkNy1mNWViYzJiNmMwOTgifQ.Y_P7L4gehl0kJwxTnwUxX8Yy502qrCHQ0hkxod58ly8

idea 控制台:
Subject: admin
Role: admin
Issue Time: Sun Aug 11 16:20:28 CST 2024
JWT ID: 9c777f0d-7459-4715-bed7-f5ebc2b6c098

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity开发抖音小游戏广告部分接入

Unity开发抖音小游戏广告部分接入 介绍环境确保开通流量主获取广告位广告部分代码测试如下总结 介绍 最近在使用Unity做抖音小游戏这块的内容&#xff0c;因为要接入广告&#xff0c;所以这里我把我接入广告的部分代码和经验分享一下。 环境确保 根据抖音官方的文档我们是先…

Linux网络编程—socket、bind

一、socket创建套接字 socket是用来创建网络通信或本地通信的套接字&#xff0c;跟文件有关&#xff1a;告诉系统&#xff0c;PCB&#xff08;进程控制块&#xff09;控制的数据应该向哪个套接字写入、或读取&#xff1b;这个套接字是在TCP/IP协议下运行的 #include <sys/t…

选择排序(附动图)

1.思路 基本思想&#xff1a; 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完 。 1.1双向选择排序&#xff08;升序&#xff09; 头尾指针&#xff08;索引&#xf…

初识C++

一、C的由来 C的起源可以追溯到1979年&#xff0c;当时Bjarne Stroustrup(本贾尼斯特劳斯特卢普&#xff0c;这个翻译的名字不同的地方可能有差异)在贝尔实验室从事计算机科学和软件工程的研究工作。面对项目中复杂的软件开发任务&#xff0c;特别是模拟和操作系统的开发⼯作&…

涉案财物管理系统DW-S405|实现人员随身物品智能化管理

涉案财物管理系统DW-S405系统基于物联网技术规范涉案财物管理流程&#xff0c;确保涉案财物的安全性、完整性和合法性&#xff1b;可以提高办案效率&#xff0c;减少办案成本&#xff0c;实现资源共享。 财物管理 管理员可通过个人账号和指纹验证两种登录方式进入财物管理系统…

1. 数据结构——顺序表的主要操作

1. 内容 顺序表的初始化、插入、删除、按值查找、输出以及其时间复杂度的计算。 2.代码 #include<stdio.h> #include<stdlib.h> //函数结果状态代码 #define OK 1 #define OVERFLOW -2 #define ERROR 0 #define MAXSIZE 100typedef int ElemType; //顺序表每个…

使用Nexus搭建Maven私服仓库

一、私服仓库简介 在Java的世界中&#xff0c;我们通常使用Maven的依赖体系来管理构件&#xff08;artifact&#xff0c;又称为二方库或三方库&#xff09;的依赖&#xff0c;Maven仓库用于存储这些构件。一般的远程仓库&#xff08;比如Maven Central&#xff09;只提供下载功…

OpenCV Python 图像处理入门

OpenCV入门 OpenCV&#xff1a;轻量、高效、开源。最广泛使用的计算机视觉工具。 下面涉及图片的读取&#xff0c;RGB彩色通道&#xff0c;区域裁剪&#xff0c;绘制图形和文字&#xff0c;均值滤波&#xff0c;特征提取&#xff0c;模板匹配&#xff0c;梯度算法&#xff0c…

获奖方案|趋动科技:资源池化释放AI算力价值

“据统计&#xff0c;GPU的平均利用率不超过30%&#xff0c;会产生巨大的算力资源浪费。我们用软件定义的方式通常可以把用户GPU的利用率提升3-8倍&#xff0c;甚至可以到10倍。” 这是算力池化软件公司趋动科技援引行业报告数据并结合自身企业最佳实践经验给出的最新数据。通…

在 SOCKS 和 HTTP 代理之间如何选择?

在 SOCKS 和 HTTP 代理之间进行选择需要彻底了解每种代理的工作原理以及它们传达的配置。只有这样&#xff0c;您才能轻松地在不同类型的代理之间进行选择。 本文概述了 HTTP 和 SOCKS 代理是什么、它们如何运作以及它们各自带来的好处。此外&#xff0c;我们将比较这两种代理类…

Java算法解析一:二分算法及其衍生出来的问题

这个算法的前提是&#xff0c;数组是升序排列的 算法描述&#xff1a; i和j是指针可以表示查找范围 m为中间值 当目标值targat比m大时&#xff0c;设置查找范围在m右边&#xff1a;i m-1 当目标值targat比m小时&#xff0c;设置查找范围在m左边&#xff1a;j m1 当targat的…

数据结构第一天

数据结构基础知识 1.1 什么是数据结构 数据结构就是数据的逻辑结构以及存储操作 (类似数据的运算) 数据结构就教会你一件事&#xff1a;如何更有效的存储数据 1.2 数据 数据&#xff1a;不再是单纯的数字&#xff0c;而是类似于集合的概念。 数据元素&#xff1a;是数据的基本单…

使用 Python 进行 PDF 文件加密

使用 Python 解密加密的 PDF 文件-CSDN博客定义一个名为的函数&#xff0c;该函数接受三个参数&#xff1a;输入的加密 PDF 文件路径input_pdf、输出的解密 PDF 文件路径output_pdf和密码password。https://blog.csdn.net/qq_45519030/article/details/141256661 在数字化时代…

优先级队列的实现

什么是优先级队列 优先级队列是一种特殊的数据结构&#xff0c;它类似于队列或栈&#xff0c;但是每个元素都关联有一个优先级或权重。在优先级队列中&#xff0c;元素的出队顺序不是简单地按照它们进入队列的先后顺序&#xff08;先进先出&#xff0c;FIFO&#xff09;&#…

虚幻5|角色武器装备的数据库学习(不只是用来装备武器,甚至是角色切换也很可能用到)

虚幻5|在连招基础上&#xff0c;给角色添加武器并添加刀光|在攻击的时候添加武器并返回背后&#xff08;第一部分&#xff0c;下一部分讲刀光&#xff09;_unreal 如何给角色添加攻击-CSDN博客 目的&#xff1a;捡起各种不同的武器&#xff0c;捡起的武器跟装备的武器相匹配 …

练习:python条件语句、循环语句和函数的综合运用

需求描述&#xff1a; 期望输出效果&#xff1a; 练习成果&#xff1a; #简单的银行业务流程 many 50000 def main_menu():print("----------主菜单----------"f"\n{name}您好&#xff0c;欢迎来到ATM&#xff0c;请选择操作&#xff1a;""\n查询余…

挑战同档位最强护眼性能,书客L2 Pro革新护眼台灯全新体验!

2024年8月17日&#xff0c;SUKER书客在今日宣布&#xff1a;书客护眼台灯L2 PRO正式发售。书客作为专业护眼台灯实力老牌&#xff0c;主打“医学养护眼”的特性&#xff0c;是唯一做到降低96%近视风险的同时&#xff0c;缓解88%用眼疲劳&#xff0c;光源99.8%高度还原自然光&am…

Ubuntu离线安装docker

查看操作系统版本&#xff1a; rootzyh-VMware-Virtual-Platform:~/install# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 24.04 LTS Release: 24.04 Codename: noble rootzyh-VMware-Virtual-Platform:~/install#…

删除镜像报子镜像依赖错误

1、删除镜像报子镜像依赖错误 出现这个错误的原因是因为有其他镜像依赖需要删除的镜像。 2解决方法 2.1首先查看无法删除的镜像被哪些镜像所依赖 docker image inspect --format{{.RepoTags}} {{.Id}} {{.Parent}} $(docker image ls -q --filter since${image_id}) # ${ima…

在阿里云上部署 Docker并通过 Docker 安装 Dify

目录 一、在服务器上安装docker和docker compose 1.1 首先关闭防火墙 1.2 安装docker依赖包 1.3 设置阿里云镜像源并安装docker-ce社区版 1.4 开启docker服务并设置开机自启动 1.5 查看docker版本信息 1.6 设置镜像加速 1.7 将docker compose环境复制到系统的bin目录下…