当前位置: 首页 > backend >正文

Java——令牌技术

目录

一、何为令牌

 JWT令牌

介绍

JWT组成

二、JWT用于验证用户登录

三、JWT令牌生成和校验

简单用法

1.创建生成密钥的方法

2.接着添加过期时间,密钥,BASE64解码密钥的属性以及生成token的方法,合并上面生成密钥的方法,下面是完整代码。

3.当然也可以写解析token的方法进行验证,通过对称密钥KEY进行对token进行验证,得到载荷部分:

进阶用法

1.创建密钥库文件(JKS)

2.读取JKS密钥库并进行签名,生成token

3.验证Token


 

一、何为令牌

令牌其实就是一个用户身份的标识,其本质上就是一个字符串。

比如我们出行在外,会带着自己的身份证,需要验证身份时,就要身份证了,而身份证不能伪造,可以辨别真假。

 服务器具备生成令牌和验证令牌的能力。

 JWT令牌

令牌本质就是一个字符串,它的实现方式有很多种,我们采用一个JWT令牌来实现。

介绍

JWT全称:JSON Web Toekn

官网:https://jwt.io/

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),用于客户端和服务器之间传递安全可靠的信息。

其本质是一个token,是一种紧凑的URL安全方法。

JWT组成

JWT由三部分组成,使用(.)分隔,比如:aaaaa.bbbbb.cccc,这三部分为:

  • Header(头部):头部包括令牌类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)
  • Payload(负载):负载部分是存放有效信息的地方,里面是一些自定义的内容。比如:{"userId":"123","userName":"zhangsan"},也可以存在jwt提供的现场字段,比如exp(过期时间戳)等 。注意:此部分不建议存放敏感信息,因为此部分能够被解码还原原始内容
  • Signature(签名):此部分用于防止jwt内容被篡改,确保安全性。

防止被篡改,而不是防止被解析。

jwt之所以安全,就是因为最后的签名。jwt当中任何一个字符被篡改,整个令牌都会校验失效。这就好比我们的身份证,之所以能标识一个人的身份,是因为不能被篡改,而不是因为内容加密。


二、JWT用于验证用户登录

具体流程如下

1.用户登录(生成JWT)

(1)用户提交凭证

        * 场景:用户输入用户名和密码,点击登录。

(2)认证服务器验证凭证

        验证流程:

                * 检查用户名和密码是否匹配数据库中的记录

                * 确认用户状态(是否被禁用)

(3)生成JWT

        JWT结构:

               * Header:声明算法类型(如"alg": "RS256", "typ": "JWT"}).

               * Payload:包含用户身份和权限信息({"sub": "user123", "roles": ["user"], "exp": 1720000000})。

                * Signature:使用私钥对头部和载荷签名 (如 RSA-SHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), privateKey))。

2.客户端存储并携带JWT

        客户端把令牌存储起来,可以存储在Cookie中,也可以存储在其他的存储空间。

3.服务器验证JWT

(1)解析JWT结构

(2)验证签名

        非对称加密流程:从认证服务器获取公钥,使用公钥重新计算签名,比对JWT中的签名是否一致。同时验证令牌是否有效,有效就说明执行了登录操作,否则就是未登录。

总结:

1.用户登录请求,经过负载均衡,把请求转给了第一台服务器,第一台服务器进行账号密码验证,验证成功后,生成一个令牌,并返回给客户端。

2.客户端收到令牌后,把令牌存储起来,可以存储在Cookie中,也可以存储在其他的存储空间。

3.用户登录成功后,携带令牌继续执行查询操作,比如查询博客列表,此时请求转发到了第二台机器 ,第二台机器会先进行权限验证操作。服务器验证令牌是否有效,就说明执行了登录操作,如果令牌是无效的,就说明用户之前未执行登录操作。


三、JWT令牌生成和校验

在使用之前先引入相关的依赖:

放在<dependencies> </dependencies>标签下:

       <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is
preferred --><version>0.11.5</version><scope>runtime</scope></dependency>

简单用法

主要思想是利用生成的对称密钥对token进行签名(加密)和验证(解密)。

下面代码主要利用测试类进行讲解:

1.创建生成密钥的方法

    @Testpublic void genKey(){//随机生成一个key,HS256算法生成 。H256是一种堆成密钥算法SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);//利用BASE64对密钥进行编码String key = Encoders.BASE64.encode(secretKey.getEncoded());System.out.println(key);}

运行后生成结果如下:

B2S6Xk0/9aDxArJCaVWEAa2moixi3RVx+O8oraRQ3cQ=

2.接着添加过期时间密钥BASE64解码密钥的属性以及生成token的方法,合并上面生成密钥的方法,下面是完整代码。

@SpringBootTest
public class JWTUtilTest {//过期毫秒时长10年public static final long Expiration =10 * 365 * 24 * 60 * 60 * 1000L;;//密钥,就是利用下面genKey()方法生成的密钥private static final String secretSrting = 
"B2S6Xk0/9aDxArJCaVWEAa2moixi3RVx+O8oraRQ3cQ=";            //生成安全密钥,BASE64解码private static final SecretKey KEY =Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretSrting));//生成token@Testpublic void genToken(){//生成载荷部分Map<String,Object> claim = new HashMap<>();claim.put("id",1);claim.put("username","zhangsan");//自定义信息String jwt = Jwts.builder().setClaims(claim)//自定义内容(负载).setExpiration(new Date(System.currentTimeMillis()+Expiration))//设置过期时间.signWith(KEY) //密钥签名.compact();System.out.println(jwt);}//生成密钥@Testpublic void genKey(){//随机生成一个key,HS256算法生成SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);String key = Encoders.BASE64.encode(secretKey.getEncoded());System.out.println(key);}}

运行genToken()方法,得到token字符串。

eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsImV4cCI6MjA2MTAwMTY0OX0.
7eFIjaWuKEZTFnBlrVgdVtplVowslGnd_VHenj3qQpU

通过得到的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).https://jwt.io/结果如下:

3.当然也可以写解析token的方法进行验证,通过对称密钥KEY进行对token进行验证,得到载荷部分:

    //校验token信息@Testpublic void parseToken(){String token ="eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsImV4cCI6MjA2MTAwMTY0OX0.7eFIjaWuKEZTFnBlrVgdVtplVowslGnd_VHenj3qQpU";//利用对称密钥再进行验证tokenJwtParser bulid = Jwts.parserBuilder().setSigningKey(KEY).build();Claims body = bulid.parseClaimsJws(token).getBody();System.out.println(body);}

运行结果如下:

进阶用法

处于安全考虑,我们可以通过RSA算法生成非对称密钥,利用私钥签名,利用公钥进行验证,同时通过还需要这对对密钥进行管理,将其管理在密钥库中。
 

下面是实现步骤:

1.创建密钥库文件(JKS)

我们需要把生成这对对 公钥-私钥 ,并将其保存在密钥库中。可以使用keytool命令创建JKS文件(在idea中的终端运行)。

命令如下:

keytool -genkeypair -alias myalias -keyalg RSA -keysize 2048 -keystore mykeystore.jks -storepass keystorepass -validity 365

命令解析:

keytool -genkeypair \-alias myalias \          # 密钥条目别名(自定义标识)-keyalg RSA \             # 密钥算法(RSA 非对称加密)-keysize 2048 \           # 密钥长度(2048 位,安全推荐值)-keystore mykeystore.jks \# 密钥库文件名(保存路径)-storepass keystorepass \ # 密钥库密码(保护整个文件)-validity 365             # 证书有效期(365 天)

此命令用于生成一个java密钥库(JKS文件),并在其中创建一个RSA密钥(非对称)对。

运行后keytool会提示输入相关信息(学习阶段随便填即可):

您的名字与姓氏是什么?[Unknown]:  localhost       # 建议填域名(如服务器域名或本地测试填 localhost)
您的组织单位名称是什么?[Unknown]:  MyOrg           # 组织单位(可选)
您的组织名称是什么?[Unknown]:  MyCompany       # 组织名称(可选)
您所在的城市或区域名称是什么?[Unknown]:  Beijing         # 城市(可选)
您所在的省/市/自治区名称是什么?[Unknown]:  Beijing         # 省份(可选)
该单位的双字母国家/地区代码是什么?[Unknown]:  CN              # 国家代码(如 CN 表示中国)
CN=localhost, OU=MyOrg, O=MyCompany, L=Beijing, ST=Beijing, C=CN是否正确?[否]:  是                  # 确认信息

输入完后,会提示输入口令,注意这个口令是和后面获取私钥的时候挂钩的,我们可以直接按回车和密钥库密码一样,同时也可以自定义。

输入完后,项目里面会生成jks文件,我们可以剪贴到resource目录下,后面需要该文件路径。

2.读取JKS密钥库并进行签名,生成token

下面是详细代码:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyStore;
import java.util.Date;
import java.util.Map;public class JWTUtils {//    EYSTORE_PASSWORD(密钥库密码):用于保护整个 JKS 密钥库文件。在加载密钥库时需要使用此密码。//配置密钥库的路径、别名。访问密码private static final String KETSTORE_PATH = "src/main/resources/mykeystore.jks";//密钥库密码private static final String KEYSTORE_PASSWORD = "keystorepass";//密钥别名private static final String KEY_ALIAS = "myalias";//访问私钥的密码(刚刚自己输入的口令)private static final String KEY_PASSWORD = "123456";//从jks文件中加载私钥private static Key getPrivateKey() throws Exception  {KeyStore keyStore = KeyStore.getInstance("JKS");try (FileInputStream keyStoreStream = new FileInputStream(KETSTORE_PATH)){keyStore.load(keyStoreStream, KEYSTORE_PASSWORD.toCharArray());}return  keyStore.getKey(KEY_ALIAS, KEY_PASSWORD.toCharArray());}//生成TOKENpublic static String generateToken(Map<String,Object> claim)throws Exception{Key privateKey = getPrivateKey();return Jwts.builder().setClaims(claim)//自定义内容(负载).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis()+60*60*1000)) //过期时间1小时.signWith(privateKey, SignatureAlgorithm.RS256) //使用私钥签名,相当于用私钥加密.compact();}
}

3.验证Token

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;public class JwtValidator {//获取公钥,公钥无需密码去访问直接获取即可private static PublicKey getpublicKey()throws  Exception{KeyStore keyStore = KeyStore.getInstance("JKS");try (FileInputStream keyStoreStream = new FileInputStream("src/main/resources/mykeystore.jks")) {keyStore.load(keyStoreStream,"keystorepass".toCharArray());}return keyStore.getCertificate("myalias").getPublicKey();}//验证JWT并解析public static Claims validateToken(String token)throws Exception{PublicKey publicKey = getpublicKey();return Jwts.parserBuilder().setSigningKey(publicKey)  //使用公钥验证.build().parseClaimsJws(token)//解析jwt.getBody();}//运行main方法执行程序public static void main(String[] args) throws Exception {//写入相关参数Map<String,Object> claim = new HashMap<>();claim.put("id",1);claim.put("username","zhangsan");String token = JWTUtils.generateToken(claim);
//        System.out.println(token);Claims claims = validateToken(token);System.out.println(claims);}
}

运行结果:


以上是JWT令牌的讲解,喜欢的支持一下,感谢观看!!!

http://www.xdnf.cn/news/2216.html

相关文章:

  • Oracle EBS 零金额的AP付款无法过账数据修复
  • 蓝桥杯赛场反思:技术与心态的双重修炼
  • 基于libdxfrw库读取样条曲线并离散为点
  • 在Linux虚拟机下使用vscode,#include无法跳转问题
  • 前端开发中shell的使用场景
  • 部署yolo到k230教程
  • cloud项目同一个服务,执行不同业务需求,nacos进行分组
  • 数据结构之单链表C语言
  • 论人际关系发展的阶段
  • Scratch——第19课 正话反说问题
  • 内存池管理项目——面试题总结
  • Linux基础指令【上】
  • 【信息系统项目管理师】高分论文:论质量管理和进度管理(智慧旅游平台建设项目)
  • springboot + mybatis 需要写 .xml吗
  • Android学习总结之Retrofit篇
  • Spring Boot 参考文档导航手册
  • 神经网络与计算机视觉
  • 创建可执行 JAR 文件
  • Go 语言中的实时交互式编程环境
  • MuJoCo 关节角速度记录与可视化,监控机械臂运动状态
  • 我们分析前端生活。
  • [Kaggle]:使用Kaggle服务器训练YOLOv5模型 (白嫖服务器)
  • 硬件须知的基本问题1
  • PowerBI动态路径获取数据技巧
  • C++如何设计线程池(thread pool)来提高线程的复用率,减少线程创建和销毁的开销
  • 微信小程序鲜花销售系统设计与实现
  • 基于STM32的物流搬运机器人
  • 【C++语法】类和对象(2)
  • 将服务器接到路由器上访问
  • 二叉堆-对顶堆 P1090-P1168-P2085