Spring - JWT

一、文档概述

1.1 概念

JSON Web Token (JWT) ,通过数字签名的方式,以JSON对象为载体,在不同的服务终端之间安全的传输信息。

有什么用

JWT最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含JWT,系统在每次处理用户请求之前,都要先进行JWT安全的校验,通过之后再进行处理。

JWT的组成

JWT由三部分组成:Header、Payload、Signature。用.连接。

eyJhbGciOiJIUZIlNiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IJRvbSIsInJvbgUiOijhZG1pbiIsInN1YiI6ImFkbWluLXR1c3QiLCJleHAiOjE2MjMyMjM2NZUsImp0aSI6ImQ2MTJjZjcxLWI5ZmUtNGMwNy04MzQwLTV¡OWViZmMyNjExNyj9.FOS9Y7rYNdc2A0idnSPrgg2XTYePUOyGZ598h2gtabE

1.2 原理

JwtProcedure.png

1.3 内容

  • 组成
  • 接入
  • 历史方案演变

二、组成

  • Header
{
    'typ': 'JWT',
    'alg': 'HS256'
}
  • Payload
{
    "sub": '123456789',
    "name": 'john',
    "admin": true
}
  • Signature
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode (payload);
var signature = HMACSHA256(encodedString, 'secret');

三、接入

依赖

pom.xml

<dependency>
    <groupId>i0.jsonwebtoken</groupId>
    <artifactid>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>

‹dependency>
    ‹groupId>com.sun.xml.bind</groupId> 
    <artifactId>jaxb-impl</artifactId>
    ‹version>2.3.0</version>
</dependency>

<dependency>
    <groupId>com. sun.xml .bind</groupId>
    ‹artifactId>jaxb-core</artifactId>
    <version>2.3.0</version>
</dependency>

‹dependency>
    <groupId>javax.activation</groupId> 
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>

实现


public class Test {

    private long time = 10000 * 60 * 60 * 24; 

    // 解密时需要signature解密
    private String signature = "admin";

    @org.junit.Test
    public void jwt() {
        JwtBuilder jwtBuilder = Jwts.builder()
        String jwtToken = jwtBuilder
                          // header
                          .setHeaderParam("typ", "JWT");
                          .setHeaderParam("alg", "HS256")
                          // payload
                          .claim("username", "tom")
                          .claim("role", "admin")
                          .setSubject("admin-test")
                          .setExpiration(new Date(System.currentTimeMillis() + time))
                          .setId(UUID.randomUUID().toString())
                          // signature
                          .signWith(SignatureAlgorithm.HS256, signature)
                          // 拼接
                          .compact();

        System.out.println(jwtToken);

    }

    @org.junit.Test
    public void parse() {
        String token = "<KEY>";
        JwtParser jwtParser = Jwts.parser();
        Jws<Claims> claimsJws = jwtParser.setSigningKey(signature).parseClaimsJws(token);

        Claims claims = claimsJws.getBody();
        System.out.println(claims.get("username"));
        System.out.println(claims.get("role"));
        System.out.println(claims.getId());
        // 签名
        System.out.println(claims.getSubject());
        // 有效期
        System.out.println(claims.getExpiration());
    }
}

四、历史方案演变

4.1 用户名&密码

每次请求都带上用户名和密钥,导致客户端必须要保存用户名和密码,这样不安全,容易导致密码泄漏。

4.2 cookie

cookie.png

服务器生成标识信息,返回给浏览器。浏览器将这些信息保存起来,每次请求都带上,服务器就可以做区分,实现记住登录状态,这就是cookie。
<font color=red>cookie的保存和发送是由浏览器自动实现的,不需要网页代码额外处理。</font>

但是数据里只有ID和名字,很有可能被人猜到,悄悄向服务器请求,拿到同样的权限。

cookie_attack.png

问题:Cookie数据被冒用、被篡改。

4.3 数据签名

需要防止Cookie数据被冒用、被篡改,我们用数据签名的方式来实现。

Eg:把所有数据连起来,拼接上存在服务器里的密令,取hash值作为签名。

cookieAsig.png

签名随同数据一并发给浏览器作为cookie。

cookieAsig2.png

<font color=red>防冒充:</font>这样后面的请求服务器就可以通过验证签名来判断数据是否合法了

cookieAsig3.png

<font color=red>注:</font>恶意攻击者不知道服务器的密令,所以很难伪造签名。

有时候我们不想让浏览器Cookie保存太多数据,可以把数据保存在服务端,并生成一个足够长的随机key用于与数据关联,然后把Key返回给浏览器写入Cookie。这样下次请求,服务器就可以根据key查找保存在服务器的内容。这个过程叫做Session,Key则是SessionID。

<font color=red>Session本质还是Cookie,唯一不同的是只给浏览器一个SessionID用作唯一身份区分,会话的数据全都保存在服务器里头,由服务器自行管理。</font>

4.4 应用互联网场景

我们的终端除了网页还有App、小程序等,这些客户端的网络请求接口默认是没有Cookie机制的。

MobileNoCookie.png

没有cookie机制,解决办法

客户端程序将“SESSION_ID”在存储系统里保存起来,然后,"SESSION_ID"换个名字,叫Token。Token和SessionID是一样,只是少了浏览器的Cookie机制,需要自己维护。
为了遵循Bearer认证规范,客户端请求时就不再使用Cookie字段,而是Authorization字段。

<font color=red>注:</font>Session和Token的会话数据都是存放在服务器的。在单机服务器下自然没有问题。

SingleServer.png

但是在分布式服务器下就会出现某些服务器没有会话数据,导致无法鉴权。

DistributeServer.png

解决办法: 在服务器再驾驶一个中心化的存储服务。例如:Redix来专门存储会话数据

CookieCenter.png

<font color=red>问题:</font>

1.这样中心化的服务容易给分布式系统造成性能瓶颈。
2.中心化服务器如果故障就会造成所有服务器连带故障。
3.边缘计算平台里头,没办法使用中心化的服务

解决办法: 会话数据还是由客户端保管,尽可能是无状态的

直接把Cookie那个数据搬过来作为Token就可以了
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容