【Spring-Security】Re12 JsonWebToken
一、认证机制种类:
1、HTTP-Basic-Auth
每次请求接口必须提供账号信息【username + password】
但是信息有暴露风险,配合RestFul风格使用,逐渐淘汰
2、Cookie-Auth
首次请求在客户端和服务端分别创建Cookie + Session 对象
通过两者的对象匹配实现状态管理,浏览器关闭会让Cookie对象销毁
可以设置Cookie过期时间
3、Open-Authorization [ Oauth ]
开放授权,授权给第三方应用来访问服务资源
4、Token-Auth
基于令牌的验证,所有权限控制的判断全部以令牌为凭证通行访问
二、Json Web Token [ JWT ]
标准描述:
https://tools.ietf.org/html/rfc7519
官网地址:
https://jwt.io/
以前后端数据交互标准的JSON作为传输载体实现Token-Auth
https://www.bilibili.com/video/BV12D4y1U7D8?p=39
三、Java - JWT
创建一个SpringBoot项目。
需要Web组件和JJWT组件两个坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
1、创建JWT
JWT令牌生成测试:
package cn.zeal4j; import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import java.util.Date; @SpringBootTest
class JJwtApplicationTests { @Test
void contextLoads() {
creatTokenTest();
} void creatTokenTest() {
JwtBuilder jwtBuilder = Jwts.builder();
jwtBuilder.
setId("8848"). // ID标识
setSubject("userL8"). // 用户主体
setIssuedAt(new Date()). // 签发时间
signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐 // 生成的JWT令牌
String jwtToken = jwtBuilder.compact();
System.out.println(jwtToken);
}
}
打印的令牌字符串:
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg1OTk5fQ.BM0CHf0kawFz6uTBjcF8aeFDYdX0M4CN0PswEPm9W0U
令牌使用点号分割令牌的各个信息
eyJhbGciOiJIUzI1NiJ9 # 头部信息
.
eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg1OTk5fQ # 荷载信息
.
BM0CHf0kawFz6uTBjcF8aeFDYdX0M4CN0PswEPm9W0U # 签发信息
2、解析JWT
解密处理:
package cn.zeal4j; import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import sun.misc.BASE64Decoder; import java.util.Date; @SpringBootTest
class JJwtApplicationTests { @Test
void contextLoads() {
creatTokenTest();
} void creatTokenTest() {
JwtBuilder jwtBuilder = Jwts.builder();
jwtBuilder.
setId("8848"). // ID标识
setSubject("userL8"). // 用户主体
setIssuedAt(new Date()). // 签发时间
signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐 // 生成的JWT令牌
String jwtToken = jwtBuilder.compact();
System.out.println(jwtToken); System.out.println("- - - - - JWT-Token-Decoder!!! - - - -");
String[] tokenParts = jwtToken.split("\\."); String tokenHead = Base64Codec.BASE64.decodeToString(tokenParts[0]); String tokenCarrier = Base64Codec.BASE64.decodeToString(tokenParts[1]); String tokenSignature = Base64Codec.BASE64.decodeToString(tokenParts[2]); System.out.println("tokenHead -> " + tokenHead);
System.out.println("tokenCarrier -> " + tokenCarrier);
System.out.println("tokenSignature -> " + tokenSignature);
}
}
输出结果:
- - - - - JWT-Token-Decoder!!! - - - -
tokenHead -> {"alg":"HS256"}
tokenCarrier -> {"jti":"8848","sub":"userL8","iat":1601386514
tokenSignature -> *�2ɥr�ԻjNz�4�����RzЂ97�+f
签名是被盐加密过了的,所以就算用算法解密了,但是有盐打乱,还是不能知道原文是什么
另外每次生成的Token信息的也会有不同的区别:
eyJhbGciOiJIUzI1NiJ9.
eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NTE0fQ.
KuQyyaVys9S7_ak56pTSWkJbtotxSetCCOTeeBitmmw
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
eyJhbGciOiJIUzI1NiJ9.
eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NzU2fQ.
LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0
头密文是一直的,但是荷载密文是在结尾有区别,原因是我们加了签发日期
而下面的签发密文是盐加密的,每次都会算出不一样的密文结果
package cn.zeal4j; import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.Base64Codec;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import sun.misc.BASE64Decoder; import java.util.Date; @SpringBootTest
class JJwtApplicationTests { @Test
void contextLoads() {
// creatTokenTest(); parseJwtTokenTest();
} void creatTokenTest() {
JwtBuilder jwtBuilder = Jwts.builder();
jwtBuilder.
setId("8848"). // ID标识
setSubject("userL8"). // 用户主体
setIssuedAt(new Date()). // 签发时间
signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐 // 生成的JWT令牌
String jwtToken = jwtBuilder.compact();
System.out.println(jwtToken); System.out.println("- - - - - JWT-Token-Decoder!!! - - - -");
String[] tokenParts = jwtToken.split("\\."); String tokenHead = Base64Codec.BASE64.decodeToString(tokenParts[0]); String tokenCarrier = Base64Codec.BASE64.decodeToString(tokenParts[1]); String tokenSignature = Base64Codec.BASE64.decodeToString(tokenParts[2]); System.out.println("tokenHead -> " + tokenHead);
System.out.println("tokenCarrier -> " + tokenCarrier);
System.out.println("tokenSignature -> " + tokenSignature);
} void parseJwtTokenTest() {
// 模拟客户端发送的JWT令牌
final String simulationClientJwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NzU2fQ.LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0"; Jws<Claims> claimsJws = Jwts.parser().setSigningKey("This is a simple salty").parseClaimsJws(simulationClientJwtToken); JwsHeader header = claimsJws.getHeader();
String keyId = header.getKeyId(); // 获取ID标识 // 负载对象
Claims body = claimsJws.getBody();
String subject = body.getSubject(); // 获取签发主体
Date issuedAt = body.getIssuedAt(); // 获取签发时间
String id = body.getId(); // 这也能获取ID? String signature = claimsJws.getSignature(); // 签名 System.out.println("from JwsHeader KeyId -> " + keyId);
System.out.println("from ClaimsBody id -> " + id); System.out.println("ClaimsBody subject -> " + subject);
System.out.println("ClaimsBody issuedAt -> " + issuedAt);
System.out.println("signature -> " + signature);
}
}
打印结果:
from JwsHeader KeyId -> null
from ClaimsBody id -> 8848
ClaimsBody subject -> userL8
ClaimsBody issuedAt -> Tue Sep 29 21:39:16 CST 2020
signature -> LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0
3、Token过期时间设置
package cn.zeal4j; import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.Base64Codec;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import sun.misc.BASE64Decoder; import java.text.SimpleDateFormat;
import java.util.Date; @SpringBootTest
class JJwtApplicationTests { @Test
void contextLoads() {
// 1、creatTokenTest(); // 2、parseJwtTokenTest(); // 3、parseExpiredJwtTokenTest(creatTokenTest()); parseExpiredJwtTokenTest(creatTokenTest());
} String creatTokenTest() {
long currentTimeMillis = System.currentTimeMillis();
long theExpireTimeMills = currentTimeMillis + (60 * 1000); // 1毫秒 * 1000(1秒) * 60 = 1 分钟
JwtBuilder jwtBuilder = Jwts.builder();
jwtBuilder.
setExpiration(new Date(theExpireTimeMills)).
setId("8848"). // ID标识
setSubject("userL8"). // 用户主体
setIssuedAt(new Date()). // 签发时间
signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐 // 生成的JWT令牌
String jwtToken = jwtBuilder.compact();
System.out.println(jwtToken); System.out.println("- - - - - JWT-Token-Decoder!!! - - - -");
String[] tokenParts = jwtToken.split("\\."); String tokenHead = Base64Codec.BASE64.decodeToString(tokenParts[0]); String tokenCarrier = Base64Codec.BASE64.decodeToString(tokenParts[1]); String tokenSignature = Base64Codec.BASE64.decodeToString(tokenParts[2]); System.out.println("tokenHead -> " + tokenHead);
System.out.println("tokenCarrier -> " + tokenCarrier);
System.out.println("tokenSignature -> " + tokenSignature); return jwtToken;
} void parseJwtTokenTest() {
// 模拟客户端发送的JWT令牌
final String simulationClientJwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NzU2fQ.LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0"; Jws<Claims> claimsJws = Jwts.parser().setSigningKey("This is a simple salty").parseClaimsJws(simulationClientJwtToken); JwsHeader header = claimsJws.getHeader();
String keyId = header.getKeyId(); // 获取ID标识 // 负载对象
Claims body = claimsJws.getBody();
String subject = body.getSubject(); // 获取签发主体
Date issuedAt = body.getIssuedAt(); // 获取签发时间
String id = body.getId(); // 这也能获取ID? String signature = claimsJws.getSignature(); // 签名 System.out.println("from JwsHeader KeyId -> " + keyId);
System.out.println("from ClaimsBody id -> " + id); System.out.println("ClaimsBody subject -> " + subject);
System.out.println("ClaimsBody issuedAt -> " + issuedAt);
System.out.println("signature -> " + signature);
} void parseExpiredJwtTokenTest(String jwtToken) {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey("This is a simple salty").parseClaimsJws(jwtToken);
// eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDEzODgzODYsImp0aSI6Ijg4NDgiLCJzdWIiOiJ1c2VyTDgiLCJpYXQiOjE2MDEzODgzMjZ9.Ve0n_TylzsCFHnk4vrjWKM_fZPGteupsx2aLbJU2E0k JwsHeader header = claimsJws.getHeader();
String keyId = header.getKeyId(); // 获取ID标识 // 负载对象
Claims body = claimsJws.getBody();
String subject = body.getSubject(); // 获取签发主体
Date issuedAt = body.getIssuedAt(); // 获取签发时间
String id = body.getId(); // 这也能获取ID? String signature = claimsJws.getSignature(); // 签名 // System.out.println("from JwsHeader KeyId -> " + keyId);
// System.out.println("from ClaimsBody id -> " + id);
//
// System.out.println("ClaimsBody subject -> " + subject);
// System.out.println("ClaimsBody issuedAt -> " + issuedAt);
// System.out.println("signature -> " + signature);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String signTime = simpleDateFormat.format(body.getIssuedAt());
String expireTime = simpleDateFormat.format(body.getExpiration());
String thisTime = simpleDateFormat.format(new Date()); System.out.println("- - - JwtTokenTimeExpireTest - - -");
System.out.println("签发时间 -> " + signTime);
System.out.println("过期时间 -> " + expireTime);
System.out.println("现在时间 -> " + thisTime);
}
}
结果打印:
- - - JwtTokenTimeExpireTest - - -
签发时间 -> 2020-09-29 22:05:26
过期时间 -> 2020-09-29 22:06:26
现在时间 -> 2020-09-29 22:05:26
超过过期时间解析Token会让程序抛出TokenExpireException异常,详细信息自测,不赘述了
4、自定义申明:
String creatTokenTest() {
long currentTimeMillis = System.currentTimeMillis();
long theExpireTimeMills = currentTimeMillis + (60 * 1000); // 1毫秒 * 1000(1秒) * 60 = 1 分钟
Map<String, Object> map = new HashMap<>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
// ......
JwtBuilder jwtBuilder = Jwts.builder();
jwtBuilder.
setExpiration(new Date(theExpireTimeMills)).
setId("8848"). // ID标识
setSubject("userL8"). // 用户主体
setIssuedAt(new Date()). // 签发时间
claim("customClaimKey01", "customClaimValue01"). // 自定义申明方式一
claim("customClaimKey02", "customClaimValue02").
addClaims(map). // 自定义申明方式二
signWith(SignatureAlgorithm.HS256, "This is a simple salty"); // 签名算法和盐
// 生成的JWT令牌
String jwtToken = jwtBuilder.compact();
System.out.println(jwtToken);
System.out.println("- - - - - JWT-Token-Decoder!!! - - - -");
String[] tokenParts = jwtToken.split("\\.");
String tokenHead = Base64Codec.BASE64.decodeToString(tokenParts[0]);
String tokenCarrier = Base64Codec.BASE64.decodeToString(tokenParts[1]);
String tokenSignature = Base64Codec.BASE64.decodeToString(tokenParts[2]);
System.out.println("tokenHead -> " + tokenHead);
System.out.println("tokenCarrier -> " + tokenCarrier);
System.out.println("tokenSignature -> " + tokenSignature);
return jwtToken;
}
void parseJwtTokenTest() {
// 模拟客户端发送的JWT令牌
final String simulationClientJwtToken = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODQ4Iiwic3ViIjoidXNlckw4IiwiaWF0IjoxNjAxMzg2NzU2fQ.LSnm3uzBW6T6fHqGLszv5jsoIoIIiKZRx_rMAwylLV0";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey("This is a simple salty").parseClaimsJws(simulationClientJwtToken);
JwsHeader header = claimsJws.getHeader();
String keyId = header.getKeyId(); // 获取ID标识
// 负载对象
Claims body = claimsJws.getBody();
String subject = body.getSubject(); // 获取签发主体
Date issuedAt = body.getIssuedAt(); // 获取签发时间
String id = body.getId(); // 这也能获取ID?
Object o = body.get("key-name"); // 获取申明
String signature = claimsJws.getSignature(); // 签名
System.out.println("from JwsHeader KeyId -> " + keyId);
System.out.println("from ClaimsBody id -> " + id);
System.out.println("ClaimsBody subject -> " + subject);
System.out.println("ClaimsBody issuedAt -> " + issuedAt);
System.out.println("signature -> " + signature);
}
【Spring-Security】Re12 JsonWebToken的更多相关文章
- 【Spring Security】七、RememberMe配置
一.概述 RememberMe 是指用户在网站上能够在 Session 之间记住登录用户的身份的凭证,通俗的来说就是用户登陆成功认证一次之后在制定的一定时间内可以不用再输入用户名和密码进行自动登录.这 ...
- 【Spring Security】六、自定义认证处理的过滤器
这里接着上一章的自定义过滤器,这里主要的是配置自定义认证处理的过滤器,并加入到FilterChain的过程.在我们自己不在xml做特殊的配置情况下,security默认的做认证处理的过滤器为Usern ...
- 【Spring Security】五、自定义过滤器
在之前的几篇security教程中,资源和所对应的权限都是在xml中进行配置的,也就在http标签中配置intercept-url,试想要是配置的对象不多,那还好,但是平常实际开发中都往往是非常多的资 ...
- 【Spring Security】四、自定义页面
在前面例子中,登陆页面都是用的Spring Security自己提供的,这明显不符合实际开发场景,同时也没有退出和注销按钮,因此在每次测试的时候都要通过关闭浏览器来注销达到清除session的效果. ...
- 【Spring Security】三、自定义数据库实现对用户信息和权限信息的管理
一 自定义表结构 这里还是用的mysql数据库,所以pom.xml文件都不用修改.这里只要新建三张表即可,user表.role表.user_role表.其中user用户表,role角色表为保存用户权限 ...
- 【Spring Security】二、数据库管理用户权限
一 引入相关的jar包 这个例子用的是mysql数据库和c3p0开源的jdbc连接池,在项目的pom.xml中引入jar包 <!-- Mysql --> <dependency> ...
- 【Spring Security】一、快速入手
一 概要 Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架.它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权.这 ...
- 【Spring Security】1.快速入门
1 导入Spring Security的相关依赖 <dependency> <groupId>org.springframework.boot</groupId> ...
- 【Spring实战】----开篇(包含系列目录链接)
[Spring实战]----开篇(包含系列目录链接) 置顶2016年11月10日 11:12:56 阅读数:3617 终于还是要对Spring进行解剖,接下来Spring实战篇系列会以应用了Sprin ...
- 【Spring开发】—— Spring Core
原文:[Spring开发]-- Spring Core 前言 最近由于一些工作的需要,还有自己知识的匮乏再次翻开spring.正好整理了一下相关的知识,弥补了之前对spring的一些错误认知.这一次学 ...
随机推荐
- C#.NET X509Certificate2 该项不适于在指定状态下使用
X509Certificate2 x509 = new X509Certificate2(lblPfxPath.Text,txtPfxPwd.Text.Trim() ); string xmlpri= ...
- Wireshark基础教程
Wireshark是非常流行的网络封包分析软件,可以截取各种网络数据包,并显示数据包详细信息.常用于开发测试过程各种问题定位.本文主要内容包括: 1.Wireshark软件下载和安装以及Wiresha ...
- work09
day10作业: 第一题: 定义一个接口Animal,包含: 1.抽象方法: run() 2.默认方法: breathe(),输出"动物会呼吸",同时调用静态方法 eat(),私有 ...
- Javascript高级程序设计第二章 | ch2 | 阅读笔记
HTML中的Javascript <script>元素 值得注意的几个关键字: async:立即开始下载脚本,仅对外部脚本有效.给脚本添加 async 属性的目的是告诉浏览器,不必等脚本下 ...
- 使用腾讯元宝+markmap生成思维导图
AI可以帮助我们进行提炼和总结, 节省了大量搜索资料和查阅的时间,像上图这张思维导图,就是使用腾讯元宝大模型进行内容提炼,再使用markmap生成思维导图,下面讲解下详细实现步骤: 一.工具准备 腾讯 ...
- thinkpad t490触摸板失灵解决方法
笔记本电脑之前触摸板使用正常,可能在某次更新之后,发现失灵不可用. 解决方法: 更新或滚动触摸板驱动程序 当您在设备管理器中时,右键单击列表中的触摸板(可能称为Dell TouchPad,Lenovo ...
- Jx9 虚拟机
一.Jx9 虚拟机的生命周期 加载 Jx9 脚本 jx9_compile() 或 jx9_compile_file(),加载编译成功后,Jx9 引擎将自动创建一个实例 (jx9_vm) 并且返回指向此 ...
- HTML5 在泛在电力物联网的 10 大业务领域 2/3D 可视化应用
过去的 2018 年,我们认为是国内工业互联网可视化的元年,图扑软件作为在工业可视化领域的重度参与者,一线见证了众多 HTML5/Web 化.2D/3D 化的项目在工业界应用落地. 2019 年可以定 ...
- 记一次 Edge 及谷歌 Chrome 浏览器兼容性冲突的解决
目录 记一次 Edge 及谷歌 Chrome 浏览器兼容性冲突的解决 浏览器兼容性冲突症状 解决方法 1. 把本机和远程的 8235 和 8237 端口屏蔽,包括 TCP 和 UDP 端口 2. 在 ...
- DDD 笔记
1. 简单讲讲DDD,和DDD哪些优势 领域驱动设计.就是通过领域来指导软件设计,是一种十分抽象的软件设计思想,它主要分为战略设计和战术设计 战略方面,通过事件风暴进行领域模型的划分,划分出核心域,子 ...