[学习笔记] 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/

评论