从零开发短视频电商 使用nimbus-jose-jwt进行对称签名和非对称签名的JWT实现

news2024/11/26 1:57:22

文章目录

    • 什么是JSON Web Token
    • 何时使用JSON Web Token
    • JSON Web Token的结构是什么
      • 头部(Header)
      • 负载(Payload)
      • 签名(Signature)
      • 拼接起来
    • 如何使用JSON Web Token
    • 工具库
    • 依赖
    • 流程
      • 对称签名
      • 非对称签名
    • 总结

JWT的基础介绍可以参见这个地址:https://jwt.io/introduction,下面都是从上面摘录的。

什么是JSON Web Token

JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于安全地在各方之间传输信息,其格式为JSON对象。这些信息可以被验证和信任,因为它们是数字签名的。

JWT可以使用对称算法(使用HMAC算法)或非对称算法(使用RSA或ECDSA的公钥/私钥)对进行签名。

尽管JWT可以加密以实现机密性(JWK),但我们更多使用的是签名令牌(JWS)。

  • 签名令牌可以验证声明的完整性,而加密令牌则隐藏这些声明,不让其他方看到。
  • 使用公钥/私钥对进行签名时,签名还证明只有持有私钥的一方才是签名者。

何时使用JSON Web Token

以下是一些适用于JSON Web Tokens的常见场景:

  • 授权:这是使用JWT的最常见场景。一旦用户登录,每个后续请求都会包含JWT,允许用户访问受该令牌允许的路由、服务和资源。单点登录是一种广泛使用JWT的功能,因为它的开销很小,并且可以轻松地在不同域之间使用。

  • 信息交换:JSON Web Tokens是安全地在各方之间传输信息的良好方式。因为JWT可以进行签名,例如使用公钥/私钥对,所以您可以确信发送方就是它们所声称的那个。此外,由于签名是使用头部和负载计算的,您还可以验证内容是否被篡改。

JSON Web Token的结构是什么

在其紧凑形式中,JSON Web Token由三个由点(.)分隔的部分组成,它们是:

  • 头部(Header)
  • 负载(Payload)
  • 签名(Signature)

因此,一个JWT通常为:xxxxx.yyyyy.zzzzz

让我们逐个解释不同的部分。

头部(Header)

头部通常由两部分组成:令牌类型(JWT)和所使用的签名算法,例如HMAC SHA256RSA

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

然后,对该 JSON 进行Base64Url编码以形成 JWT 的第一部分。

负载(Payload)

令牌的第二部分是有效载荷,其中包含声明(claims)。声明是关于实体(通常是用户)和附加数据的陈述。有三种类型的声明:注册声明、公共声明和私有声明。

  • 注册声明:这是一组预定义的声明,它们不是强制性的,但建议使用,以提供一组有用的、可互操作的声明。其中一些包括:iss(发行人)、exp(过期时间)、sub(主题)、aud(受众)等。

请注意,声明名称只有三个字符长,因为JWT旨在紧凑。

  • 公共声明:这些声明可以由使用JWT的人自由定义。但为了避免冲突,它们应在IANA JSON Web Token注册表中定义,或者定义为包含冲突安全命名空间的URI。

  • 私有声明:这些是自定义的声明,用于在同意使用它们的各方之间共享信息,既不是注册声明也不是公共声明。

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

然后通过Base64Url编码,形成JSON Web Token的第二部分。

请注意,对于已签名的令牌,尽管受到篡改的保护,但任何人都可以读取此信息。除非进行加密,否则不要将机密信息放在JWT的有效载荷或头部元素中。

签名(Signature)

要创建签名部分,您需要获取编码的头部、编码的有效载荷、一个密钥、头部中指定的算法,并对其进行签名。

例如,如果您想使用HMAC SHA256算法,签名将按以下方式创建:

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

签名用于验证消息在传递过程中是否被更改,并且对于使用私钥签名的令牌,还可以验证JWT的发送者身份。

拼接起来

输出是由点分隔的三个Base64-URL字符串,可以在HTML和HTTP环境中轻松传递,与基于XML的标准(如SAML)相比更紧凑。

以下是一个具有先前编码的头部和有效载荷,并使用密钥签名的JWT的示例。

Encoded JWT

如何使用JSON Web Token

在身份验证中,当用户使用其凭据成功登录时,将返回 JSON Web 令牌。需要注意的是,不应将JWT保存的时间超过必要的时间,因为它们是需要保护的凭据。此外,由于安全性的缺乏,应避免将敏感会话数据存储在浏览器存储中。

令牌传输:当用户想要访问受保护的路由或资源时,通常将JWT包含在请求中,放置在授权头部中,使用Bearer方案。头部的内容应如下所示:

Authorization: Bearer <token>

服务器的受保护路由会检查授权头部中是否存在有效的JWT,如果存在,则允许用户访问受保护的资源。如果JWT包含必要的数据,则可以减少某些操作对数据库的查询需求,尽管这并非总是如此。

请注意,如果通过HTTP头部发送JWT令牌,应尽量防止其过大。某些服务器不接受超过8 KB的头部。

工具库

可以在这个网址查找比较权威好用的工具库。

  • maven: com.auth0 / java-jwt / 3.3.0
  • maven: io.jsonwebtoken / jjwt-root / 0.11.1
  • maven: com.nimbusds / nimbus-jose-jwt / 5.7

大部分都是使用上面3个中的某一个,我们这里使用nimbus-jose-jwt。

文档:https://connect2id.com/products/nimbus-jose-jwt

依赖

<dependency>
  <groupId>com.nimbusds</groupId>
  <artifactId>nimbus-jose-jwt</artifactId>
  <version>9.31</version>
</dependency>
<!-- 如果您正在使用以下情况,请取消注释下面的依赖项:
     - JDK 10或更早版本,并且您想要使用RSASSA-PSS(PS256、PS384、PS512)签名算法。
     - JDK 10或更早版本,并且您想要使用EdECDH(X25519或X448)椭圆曲线迪菲-赫尔曼密钥交换加密。
     - JDK 14或更早版本,并且您想要使用EdDSA(Ed25519或Ed448)椭圆曲线签名算法。
     在JDK 15或更高版本上,这些算法是不必要的。
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
    <scope>runtime</scope>
</dependency>
-->

最新版本

流程

对称签名

加签示例

// 生成对称加密密钥
 byte[] sharedKey = "YourSharedKey-122345678sahkjhjkasdfasdf".getBytes();

// 创建一个JWT对象
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject("user123")
                // 设置过期时间为当前时间后的一分钟
                .expirationTime(new Date(System.currentTimeMillis() + 60 * 1000)).build();

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS256).build();
SignedJWT signedJWT = new SignedJWT(header, claimsSet);

// 创建HMAC签名器
JWSSigner signer = new MACSigner(sharedKey);

// 对JWT进行签名
signedJWT.sign(signer);

// 将JWT序列化为字符串
String jwtString = signedJWT.serialize();

System.out.println("JWT Token: " + jwtString);

验签示例

// 生成对称加密密钥
 byte[] sharedKey = "YourSharedKey-122345678sahkjhjkasdfasdf".getBytes();

// 解析JWT字符串
String jwtString = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2ODg1NDY2MDAsInN1YiI6InVzZXIxMjMifQ.3mGtNjwt6Z50DhEeBv2zo9qi8aHGh9Mu2RWLVeH0FE8";
SignedJWT signedJWT = SignedJWT.parse(jwtString);
// 创建HMAC验证器
JWSVerifier verifier = new MACVerifier(sharedKey);
// 验证JWT签名
boolean isValid = signedJWT.verify(verifier);

if (isValid) {
      System.out.println("JWT signature is valid.");
      // 获取JWT的声明
      JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
      System.out.println("Subject: " + claimsSet.getSubject());
      System.out.println("Expiration Time: " + claimsSet.getExpirationTime());

      if (!signedJWT.getJWTClaimsSet().getExpirationTime().after(new Date())) {
                System.out.println("JWT signature is expired.");
        }
} else {
            System.out.println("JWT signature is not valid.");
}

非对称签名

RSA算法和ECDSA (Elliptic Curve Digital Signature Algorithm)算法是常用的非对称加密算法,用于生成和验证数字签名。

RSA算法是基于大素数分解的数论问题。它使用一对公钥和私钥来进行加密和解密操作,同时也可以用于生成和验证数字签名。RSA算法在安全性和广泛应用上都有很好的表现,但由于其计算复杂性,对于大数据量的加密和解密操作可能会比较耗时

ECDSA算法基于椭圆曲线离散对数问题。相比于RSA算法,ECDSA算法使用更短的密钥长度,提供相同的安全性水平。这使得ECDSA算法在资源受限的环境中更具优势,如移动设备和物联网设备。ECDSA算法还具有更快的加密和解密速度

如何生成EC384公私钥

方式一

// Generate an EC key pair
ECKey ecJWK = new ECKeyGenerator(Curve.P_384)
                .keyID("123")
                .generate();
ECPublicKey ecPublicKey = ecJWK.toECPublicKey();
ECPrivateKey ecPrivateKey = ecJWK.toECPrivateKey();
// 将公钥编码为Base64字符串
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
// 将私钥编码为Base64字符串
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded())    

//  或者
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;

// 生成ECDSA密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
keyPairGenerator.initialize(384); // 使用EC384曲线
KeyPair keyPair = keyPairGenerator.generateKeyPair();

// 获取私钥和公钥
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();

// 打印私钥和公钥
System.out.println("Private Key: " + privateKey);
System.out.println("Public Key: " + publicKey);
// 将公钥编码为Base64字符串
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
// 将私钥编码为Base64字符串
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded())    

publicKeyBase64: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEVT+YNmKBnvXtS11FvcKe7tBHi3aAbvk87+tBGFadfHM/zy1+Q4EjlXjbLhhl1LNPup5BHhQBG+jKRP0/Rvoy0LiNmDdX9MqC0xvTtefFKBL4CsM0vlViObOUNxumzxMH
privateKeyBase64: ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDDAaCeLDnCRmkmZ8vs7nlnApCxBIL2RyizpY4jh1VE5Svr4d92AwjZyrt5Szl8AvPE=

方式二

openssl ecparam -list_curves
# 生成私钥
openssl ecparam -genkey -name secp384r1 -noout -out ec384-private.pem
# 根据私钥生成公钥
openssl ec -in ec384-private.pem -pubout -out ec384-public.pem
# 把私钥转换为PKCS8格式
openssl pkcs8 -topk8 -nocrypt -in ec384-private.pem -out ec384-private.pem_pkcs8.pem
# 注意
publicKeyBase64 = ec384-public.pem中的字符串
privateKeyBase64 = ec384-private.pem_pkcs8.pem中的字符串

序列化,反序列化和传输公私钥

注意,私钥一定是颁发者自己好好保存,公钥的话无所谓,公钥本来就是要公开的。可以通过微信邮件等传输。

ECDSA公钥可以以多种格式进行存储和传输。以下是使用Base64编码的示例:

// 假设已经有Base64编码的公钥和私钥字符串
String publicKeyBase64 = "YourBase64EncodedPublicKey";
String privateKeyBase64 = "YourBase64EncodedPrivateKey";
// 将Base64字符串解码为字节数组
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);

// 创建公钥的KeySpec对象
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);

// 创建私钥的KeySpec对象
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);

// 使用KeyFactory生成公钥和私钥对象
KeyFactory keyFactory = KeyFactory.getInstance("EC");
ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(publicKeySpec);
ECPrivateKey privateKey = (ECPrivateKey) keyFactory.generatePrivate(privateKeySpec);

System.out.println("Public Key: " + publicKey);
System.out.println("Private Key: " + privateKey);

加签示例

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;

// 创建一个JWT对象
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
        .subject("user123")
        .expirationTime(new Date(new Date().getTime() + 60 * 1000)) // 设置过期时间为当前时间后的一分钟
        .build();

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES384)
        .build();

SignedJWT signedJWT = new SignedJWT(header, claimsSet);

// 创建ECDSA私钥签名器
JWSSigner signer = new ECDSASigner(privateKey);

// 对JWT进行签名
signedJWT.sign(signer);

// 将JWT序列化为字符串
String jwtString = signedJWT.serialize();

System.out.println("JWT Token: " + jwtString);

验签示例

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;

// 解析JWT字符串
SignedJWT signedJWT = SignedJWT.parse(jwtString);

// 创建ECDSA公钥验证器
JWSVerifier verifier = new ECDSAVerifier(publicKey);

// 验证JWT签名
boolean isValid;
try {
    isValid = signedJWT.verify(verifier);
} catch (JOSEException e) {
    isValid = false;
}

if (isValid) {
    System.out.println("JWT signature is valid.");

    // 获取JWT的声明
    JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
    System.out.println("Subject: " + claimsSet.getSubject());
    System.out.println("Expiration Time: " + claimsSet.getExpirationTime());
} else {
    System.out.println("JWT signature is not valid.");
}

总结

对称签名适用于以下情况:

  1. 快速性能要求:对称签名算法通常比非对称签名算法更快,因为它们使用相同的密钥进行签名和验证。
  2. 内部通信:当签名用于内部通信,不需要在不同的实体之间共享密钥时,对称签名是一种简便的选择。
  3. 密钥管理:对称签名只需要管理一个密钥,而非对称签名需要管理公钥和私钥对。

非对称签名适用于以下情况:

  1. 安全性要求:非对称签名提供更高的安全性,因为它使用不同的密钥进行签名和验证,私钥保持私密,公钥可公开共享。
  2. 跨网络通信:当签名用于跨网络通信,需要在不同的实体之间共享公钥时,非对称签名是更安全的选择。
  3. 数字证书:非对称签名用于生成和验证数字证书,以确保通信的身份验证和数据的完整性。

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

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

相关文章

超实用攻略!GPT能玩的这么6,你居然还不知道?

开篇 自古以来,智者皆知学无止境,而在我们身边,正有一款奠基于这个原则的AI机器人—ChatGPT,他擅长从网络上学习各种知识,然后把这些知识用在他的对话中。没错,它就是天马行空的闲话家,无所不谈的取经者。可你知道怎样让它更加符合你的使用需求,适应你的工作节奏么?哦…

Qt Quick系列(9)—初识画布

&#x1f680;作者&#xff1a;CAccept &#x1f382;专栏&#xff1a;Qt Quick 文章目录 前言代码示例1源码关键知识点 代码示例2源码关键知识点 总结 前言 画布元素的基本思想是使用上下文2D对象&#xff08;ctx&#xff09;渲染路径。上下文2D对象包含必要的图形功能&…

一文读懂FPC(12)- FPC的阻抗控制

FPC系列文章目录 1.什么是FPC 2.什么是R-FPC 3&#xff0c;FPC的基材 4.FPC基材压延铜和电解铜的区别 5&#xff0c;FPC的辅材 6&#xff0c;FPC常见的四种类型 7&#xff0c;FPC的生产流程简介 8&#xff0c;R-FPC的生产流程简介 9&#xff0c;FPC的发展及应用 10&a…

Vue :在 VSCode 中安装 yarn 并用 yarn 工具来控制 Vue 项目的详细过程

Ⅰ、 Yarn 工具简介&#xff1a; 1、什么是 yarn 工具: Yarn 是 facebook 发布的一款取代 npm 的资源包管理工具&#xff0c;是一个快速、可靠、安全的依赖管理工具&#xff0c;一款新的 JavaScript 资源包管理工具(吐槽下&#xff1a;最大的弊端是&#xff0c;要通过 npm 来…

java中的String使用注意事项、StringBuilder、StringBuffer、StringJoiner

String使用注意事项 这里第二次创造了0个对象是因为字符串常量池中已经有"abc" StringBuilder 注意StringBuilder支持链式编程 StringBuilder s new StringBuilder("abc"); s.append(666).append("acb" ).append(666); System.out.printl…

89、基于STM32单片机激光测距仪远距离倒车防撞报警器系统设计(程序+原理图+PCB源文件+参考论文+硬件设计资料+元器件清单等)

单片机主芯片选择方案 方案一&#xff1a;AT89C51是美国ATMEL公司生产的低电压&#xff0c;高性能CMOS型8位单片机&#xff0c;器件采用ATMEL公司的高密度、非易失性存储技术生产&#xff0c;兼容标准MCS-51指令系统&#xff0c;片内置通用8位中央处理器(CPU)和Flash存储单元&a…

操作系统8:存储器的层次结构及程序的装入和链接

目录 1、存储器的层次结构 &#xff08;1&#xff09;多层结构的存储器系统 1.1 - 存储器的多层结构 1.2 - 可执行存储器 &#xff08;2&#xff09;主存储器与寄存器 2.1 - 主存储器 2.2 - 寄存器 &#xff08;3&#xff09;高速缓存和磁盘缓存 3.1 - 高速缓存 3.2 …

使用wordpress搭建WebStack导航网站记录

0 序言 首先&#xff0c;我来介绍下&#xff0c;这个webstack导航网站实际上是被做成了wordpress的一个主题&#xff0c;具体这个主题的下载地址如下&#xff1a; WordPress 版 WebStack 导航主题https://github.com/owen0o0/WebStack 我们不需要使用git clone命令&…

【观察者模式】 ——每天一点小知识

&#x1f4a7; 观察者模式 \color{#FF1493}{观察者模式} 观察者模式&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&#x1f995;…

uni-calendar 日历控件自定义点的颜色功能

增加自定义点的颜色功能&#xff0c;官方空间点的颜色是固定红色 1、修改的页面 uni-calendar-item 在uni-calendar-item页面中的这个标签中加上一个style属性&#xff0c;判断传过来的参数是否存在bgcolor背景颜色&#xff0c;有的话添加背景色&#xff0c;没有则不添加默认系…

【DBA课程-笔记】第 4 章:MongoDB数据库管理备份

课程大纲 MongoDB备份机制数据库的恢复数据库的导出数据库的导入MongoDB面试题 一、MongoDB备份机制 1. MongoDB备份方式 磁阵列文件系统快照 Filesystem Snapshots复制数据文件 CPmongodump 备份 和 mongorestore 恢复工具mongoimport 导入 和 mongoexport 导出工具MongoD…

Qt - 模型视图

模型/视图 概念 模型/视图结构将数据存储和界面展示分离&#xff0c;分别用不同的类实现 模型&#xff1a;存储数据视图&#xff1a;界面上的视图组件显示模型中的数据&#xff1b;在视图组件里修改的数据会被自动保存在模型中 源数据 内存中的一个字符串列表 磁盘文件系统结构…

【二叉树part08】| 669.修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

目录 &#x1f388;LeetCode669. 修剪二叉搜索树 &#x1f388;LeetCode108.将有序数组转换为二叉搜索树 &#x1f388;LeetCode538.把二叉搜索树转换为累加树 &#x1f388;LeetCode669. 修剪二叉搜索树 链接&#xff1a;669.修剪二叉搜索树 给你二叉搜索树的根节点 root…

用于 3D 建模和渲染应该选择怎么样的配置?

选择合适的机器可能会使决定变得困难。在分析图像时&#xff0c;它还需要一定数量的核心和线程&#xff0c;这绝对是一个怪物。渲染一般是使用GPU或者CPU来进行&#xff0c;从而产生渲染效果。 3D 建模是一种用于构建和评估实际物理对象的虚拟 3D 模型的应用程序。该技术应用于…

Mvc进阶(下)

Mvc进阶&#xff08;下&#xff09; 1.前言2.上次代码弊端1.利用xml建模反射优化1.XMl文件2.对xml建模 3.修改中央控制器 3.再优化1.先优化Action子控制器4.优化传值问题 4.总结 1.前言 虽然前面文章深入解析Java自定义MVC框架的原理与实现讲述了Mvc框架&#xff0c;但是那只能…

suse ha for sap scale-up性能优化场景安装配置

1. 安装SUSE操作系统 在官网下载SUSE Linux Enterprise Server for SAP Applications安装介质&#xff0c;在安装操作系统过程中&#xff0c;选择SUSE Linux Enterprise Server for SAP Applications操作系统。 在软件选择界面&#xff0c;根据需要选择SAP HANA Server Base…

oracle connect by很强,但是要慎用,不然有你哭的时候

前言: 第四次工业革命&#xff0c;带来了科技的巨大变更&#xff0c;同时带来了很多半结构化数据&#xff0c;很多数据会做成集合、JSON的形式存储到数据库中&#xff0c;通过ETL工具我们将这些数据抽取到数仓里面&#xff0c;我们怎么进行分析呢&#xff1f;这些数据类似这样的…

centos7安装git及maven

安装git 直接使用yum安装&#xff0c;指令如下&#xff1a; yum install git然后执行如下指令判断是否安装完成&#xff1a; git --version紧接着需要维护git的用户名及邮箱等信息 git config --global user.name "zzy" git config --global user.email "ex…

JS知识点汇总(十四)--事件循环

1. 对事件循环的理解 JavaScript 在设计之初便是单线程&#xff0c;即指程序运行时&#xff0c;只有一个线程存在&#xff0c;同一时间只能做一件事 JavaScript 初期作为一门浏览器脚本语言&#xff0c;通常用于操作 DOM &#xff0c;如果是多线程&#xff0c;一个线程进行了删…

QT学习笔记:调整控件大小和位置

前面的文章&#xff0c;我讲了怎么用layout去布局。但布局做完后&#xff0c;发现界面有点怪。比如&#xff0c;最低下的“清除”按钮这么大&#xff0c;“消息体”这个label没有位于中间等。下面&#xff0c;我就来讲下怎么把界面继续优化。 1、调整“清除”按钮大小和位置 …