[学习笔记] SpringSecurity之认识JWT
# 学习 # · 2021-10-26
常见的认证机制
1、HTTP Basic Auth:配RESTful API使用的最简单的认证方式,只需提供用户名密码即可,但由于有把用户名密码暴露给第三方客户端的风险,在生产环境下被使用的越来越少。
2、Cookie Auth:为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象。通过客户端带上来Cookie对象来与服务器端的Session对象匹配来实现状态管理的。
3、OAuth:是一个开放的授权标准,允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源,而无需将用户名和密码提供给第三方应用。如网站通过微信、QQ登录等。
4、Token Auth:
(1)客户端使用用户名跟密码请求登录。
(2)服务端收到请求,去验证用户名与密码。
(3)验证成功后,服务端会签发一个Token,再把这个Token 发送给客户端。
(4)客户端收到Token以后可以把它存储起来,比如放在Cookie里。
(5)客户端每次向服务端请求资源的时候需要带着服务端签发的Token。
(6)服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据。
JWT
1、JWT:JSON Web Token,是一个开放的行业标准,它定义了一种简介的、自包含的协议格式,用于在通信双方传递JSON对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。
2、JWT令牌的特点:
(1)JWT基于JSON,非常方便解析。
(2)可以在令牌中自定义丰富的内容,易扩展。
(3)通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
(4)资源服务使用JWT可不依赖认证服务即可完成授权。
(5)JWT令牌较长,占存储空间比较大。
3、JWT的组成:头部(Header)+载荷(Payload)+签名(Signature)。
(1)头部:用于描述关于该JWT的最基本的信息,例如其类型(typ)、签名所用的算法(alg)等。
{
"alg": "HS256",
"typ": "JWT"
}
(2)载荷:存放有效信息的地方。
/**
1、标准中注册的声明:建议但不强制使用。
iss: JWT签发者
sub: JWT所面向的用户
aud: 接收JWT的一方
exp: JWT的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的
iat: JWT的签发时间
jti: JWT的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
2、公共的声明:可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息。
3、私有的声明:私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息。
**/
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1635661084
}
(3)签名:使用base64(Header) + base64(Payload)连接组成字符串,再使用在头部定义的签名所用的算法对连接组成的字符串进行加盐(Secret)组合加密得到签名。
(4)JWT=base64(Header) + base64(Payload) + Signature。
JJWT
1、JJWT:是一个提供端到端的JWT创建和验证的Java库。
2、JJWT快速入门:
(1)创建SpringBoot项目,导入相关依赖。
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
(2)在测试类中编写代码:
@Test
void contextLoads() {
// 创建JwtBuilder对象
JwtBuilder jwtBuilder = Jwts.builder()
// 声明JWT的唯一身份标识:{"jti":"demo"}
.setId("demo")
// 声明JWT所面向的用户:{"sub":"username"}
.setSubject("username")
// 声明JWT的签发时间:{"iat":"xxx"}
.setIssuedAt(new Date())
// 声明签名所用的算法
.signWith(SignatureAlgorithm.HS256,"salt");
// 获取JWT的token
String token = jwtBuilder.compact();
System.out.println("JWT Token:" + token);
}
3、Token验证解析:
@Test
public void testParseToken(){
// 获取Token
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJkZW1vIiwic3ViIjoidXNlcm5hbWUiLCJpYXQiOjE2MzU2NjUzMDF9.YkDjb1R2RLeNPiUZAV_WsOnnduzAiXgYhZYwgNASxbE";
// 解析token获取负载中的声明对象
Claims claims = Jwts.parser()
.setSigningKey("salt")
.parseClaimsJws(token)
.getBody();
// 打印声明的属性
System.out.println("JWT的唯一身份标识:" + claims.getId());
System.out.println("JWT所面向的用户:" + claims.getSubject());
System.out.println("JWT的签发时间:" + claims.getIssuedAt());
}
4、JWT过期校验:
(1)定义Token过期时间:
@Test
void contextLoads() {
// 获取当前系统时间的长整型
long now = System.currentTimeMillis();
// 定义过期时间,这里是1分钟后的时间长整型
long exp = now + 60 * 1000;
// 创建JwtBuilder对象
JwtBuilder jwtBuilder = Jwts.builder()
// ...
// 设置过期时间
.setExpiration(new Date(exp));
// 。。。
}
(2)解析Token过期时间:
@Test
public void testParseToken(){
// ...
// 打印声明的属性
DateFormat sf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("签发时间:"+sf.format(claims.getIssuedAt()));
System.out.println("过期时间:"+sf.format(claims.getExpiration()));
System.out.println("当前时间:"+sf.format(new Date()));
}
5、自定义claims:
(1)添加自定义声明:
@Test
void contextLoads() {
// ...
// 创建JwtBuilder对象
JwtBuilder jwtBuilder = Jwts.builder()
// ...
// 自定义声明
.claim("roles","admin")
.claim("avatar","avatar.jpg");
// ...
}
(2)解析自定义声明内容:
@Test
public void testParseToken(){
// ...
// 打印声明的属性
System.out.println("roles:" + claims.get("roles"));
System.out.println("avatar:" + claims.get("avatar"));
}
如若转载,请注明出处:一木林多 - https://www.l5v.cn/archives/330/
评论