JWT令牌技术

什么是令牌技术

令牌技术是一种重要的安全技术,它在多个领域中发挥着关键作用。简单来说,令牌(Token)可以被视为系统的临时密钥,相当于账户名和密码,用于决定是否允许特定的请求,并判断该请求来自哪个用户。

在网络安全领域,令牌技术被广泛应用。它允许用户在不提供密码或其他凭证的前提下,访问网络和系统资源。这些令牌在系统中持续存在,直到系统重新启动。令牌的一个显著特点是其随机性和不可预测性,这使得黑客或恶意软件很难猜测出令牌的内容。

在支付领域,令牌技术则表现为一种虚拟账号技术。它将用户持有的信用卡或借记卡的16位主账号转化为一个与之对应的虚拟账号,并与具体的设备(如智能手机)绑定。如果发生黑客入侵事件,用户的真实卡号已经被令牌技术转化为虚拟账号,即使黑客获取了这个电子令牌,也无法直接利用。此外,如果用户的设备丢失,金融机构可以简单地使该设备上的电子令牌失效,从而保护用户的账户安全。

以苹果支付为例,它正是应用了令牌技术。这种技术不仅提高了支付的安全性,还大大节约了成本,因为金融机构无需在每次安全风险事件发生后都发放新的实体卡。

此外,OTP(一次性密码)令牌也是一种令牌技术的体现。它采用一种算法,每隔一段时间(如60秒)生成一个不可预测的随机数字组合,作为一次性的登录密码。这种技术增强了账号的安全性,因为它避免了定期更换密码的繁琐,同时也减少了密码被猜测或破解的风险。

简单点来说:令牌技术就是一个用户身份的标识,名称起的很高大上,其实本质上就是一个字符串。

为什么需要令牌技术呢

给大家举个例子,对于一个用户量很大的软件来说,它的服务器的数量肯定不止一个,因为如果只有一个服务器的话,对于多个客户端发来的请求,一个服务器短时间内是无法及时处理这些请求的话,如果在短时间内客户端的请求数量达到了服务器能处理的最大数量的请求的话,那么这个服务器就可能会发生宕机的问题,那么所有客户端的请求就无法得到处理,出现这样的问题就会造成很大的损失。

所以为了避免出现单个服务器出现宕机的问题,一个应用的背后会有多个服务器,并且通过负载均衡的做法来将不同数量的请求分配给指定的服务器,如果一个服务器当时可以接收的请求量较大的话,通过负载均衡的做法该服务器就可以得到较多数量的在承受范围之内的请求数量,对应的,对于那些当时可以接收的请求数量较少的服务器来说,通过负载均衡的操作该服务器得到的请求的数量就较少,这样就极大的降低了服务器出现宕机的问题。

在这里插入图片描述

但是通过负载均衡的做法的话,又会出现其他的问题,对于网站上用户的操作,往往需要先对用户的登录状态进行判断,对于一些操作只有用户登录之后才可以进行,而通常的判断用户是否登陆的的方法就是通过是否存在 Session 会话来判断,当用户成功登录之后呢,就创建出类似哈希表的结构将 Session 存储在服务器本地,然后通过响应将 SessionId 返回给客户端,并且存储在 Cookie 中,下一次客户端发送请求之后,就会将这个 SessionId 发送给服务器,服务器拿到请求的数据包之后,就可以根据这个 SessionId 来判断用户是否登录了。但是既然 Session 会话是存储在服务器本地,那么当有多个服务器的时候,如果用户前两分钟发送的登录请求是在服务器 A 中处理的话,那么当前会话就是在服务器 A 的本地存储的,那么如果过了两分钟之后用户又发送了其他操作请求的话,这时因为负载均衡的操作,该请求被分配到服务器 B 中去处理的话,服务器 B 就会先判断用户是否登录,根据客户端发来的请求中的 SessionId 来获取会话,但是因为服务器 B 中没有存储对应的会话,所以就会强制用户进行登录,这时用户就会问了:我不是刚刚登录了吗?为什么又让我登录呢?这是哪个xx写的程序。这样就会极大的影响用户的使用体验。

所以为了解决这个问题就出现了令牌技术,令牌技术会将令牌存储在客户端的浏览器上,而不是服务器上,当客户端发送请求的时候,将这个令牌也发送给服务器,服务器得到这个令牌之后就会对这个令牌进行解析,如果解析成功就可以认定该用户处于登录状态,反之则强制用户进行登录。

给大家举个例子:在之前网络还不发达的时候,如果我们需要住酒店的时候,我们进去酒店的时候需要将身份证交给负责人员,但是当我们进行其他也需要身份证的操作的时候,因为我们的身份证在酒店负责人那里,所以我们就需要先去酒店负责人那里拿身份证然后才能继续,但是现在,我们的身份信息都被录入了公安系统,我们需要住酒店,只需要刷身份证之后,身份证就会归还给我们了,那么我们就可以去干其他的事了,这也就是能证明自己身份的信息在用户手中的好处。

JWT 令牌

实现令牌技术的方法有很多种,今天我为大家分享的令牌技术是 JWT 令牌技术(Json Web Token),JWT官网

JWT 组成

JWT 由三个部分组成,每部分中间使用点 . 分隔:

  • Header(头部):头部包括令牌的类型(既JWT)及使用的哈希算法
  • Payload(负载):负载部分是存放有效信息的方法,里面是一些自定义的内容。此部分不建议存放敏感信息,因为此部分可以解码还原原始内容
  • Signature(签名):此部分用于防止JWT内容被篡改,确保安全性

注意了签名是为了防止被篡改,而不是为了防止被解析,JWT之所以安全就是因为最后的签名。JWT令牌当前任何一个字符被篡改,整个令牌都会校验失败,就好比我们的身份证,它的目的不是为了不让别人知道,而是保证了我们的身份信息不会被篡改,人们很容易看到我们身份证上的信息,JWT也是。

在这里插入图片描述

当我们修改 Token 之后,再对其进行解析的时候,就会发现:该 Token 无法解析:

在这里插入图片描述

JWT 令牌的使用

1. 引入 JWT 依赖

首先因为 JWT 令牌属于第三方库,所以需要先引入 JWT 依赖:

<!-- 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>

生成 JWT 令牌

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest
public class JwtUtilTest {
    //设置令牌的过期时间为30分钟
    private static final long expiration = 1000 * 60 * 30;
    //密钥
    private static final String secretString = "fahfjkshfshdjfhskhfksjdhfjsdjfhsjkdfhs";
    //生成安全密钥
    private static final SecretKey KEY = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretString));

    @Test
    public static void genJwt() {
        //自定义信息
        Map<String, Object> claim = new HashMap<>();
        claim.put("id",1);
        claim.put("username","zhangsan");

        String jwt = Jwts.builder()
                .setClaims(claim) //自定义内容(负载)
                .setIssuedAt(new Date()) //设置签证时间
                .setExpiration(new Date(System.currentTimeMillis() + expiration)) //设置过期时间
                .signWith(KEY) //签名算法
                .compact();

        System.out.println(jwt);
    }

    public static void main(String[] args) {
        JwtUtilTest.genJwt();
    }
}

当我们运行这个方法之后,就会报下面这个错误,原因是,我们的密钥的长度太短了,并且 idea 建议我们使用 io.jsonwebtoken.security.Keys#secretKeyFor(SignatureAlgorithm)方法来生成一个密钥:

java.lang.ExceptionInInitializerError
Caused by: io.jsonwebtoken.security.WeakKeyException: The specified key byte array is 224 bits which is not secure enough for any JWT HMAC-SHA algorithm.  The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a size >= 256 bits (the key size must be greater than or equal to the hash output size).  Consider using the io.jsonwebtoken.security.Keys#secretKeyFor(SignatureAlgorithm) method to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm.  See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.
	at io.jsonwebtoken.security.Keys.hmacShaKeyFor(Keys.java:96)
	at JwtUtilTest.<clinit>(JwtUtilTest.java:19)
Exception in thread "main" 

所以我们就先使用 idea 建议的方法来生成一个密钥:

public static void genKey() {
    Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    String secretString = Encoders.BASE64.encode(key.getEncoded());
    System.out.println(secretString);
}

HS256 代指对应的编码方式:

在这里插入图片描述

我们运行这个方法获得一个符合规范的密钥:

在这里插入图片描述

private static final String secretString = "ZzvPBGOUCmilHmOl6jNmAbusvQryFipKn4VA1yrP7e0=";

base64编码之后的字符串应该只包含 A-Z, a-z, 0-9, ‘-’ 和 ‘_’ 这些字符。

根据生成的密钥生成 Token:

在这里插入图片描述

eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsImlhdCI6MTcxMDA3NjI0OSwiZXhwIjoxNzEwMDc4MDQ5fQ.PzZoPdXaL2Guc3IZDt2m_-bmaIwu9GzgivTwus36Qno

解析 JWT 令牌

当我们使用相应的 API 生成 JWT 之后,客户端浏览器将服务器生成的令牌保存在浏览器中,下次发送请求的时候就将令牌传给服务器,服务器得到之后就需要对这个令牌进行解析。

我们将前面生成的 JWT 放在 JWT 解码网站上进行解析之后就可以得到我们的载荷部分,exp表示过期时间:

在这里插入图片描述
但是可以看到密钥部分是无法解析出来的,别人拿不到对应的密钥就无法伪造出 Token。

然后我们看看如何对 Token 进行解析:

public static void parseJwt() {
    String token = "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsImlhdCI6MTcxMDA3OTExMSwiZXhwIjoxNzEwMDgwOTExfQ.0_hCtlyhAPoQYnbyCvcKGDGS78YS6jxjNfrFtJ--E9M";
    Claims claims = Jwts.parserBuilder()
            	.setSigningKey(secretString)
            	.build()
                .parseClaimsJws(token)
                .getBody();
    System.out.println(claims);
}

在这里插入图片描述
令牌解析之后我们就可以看到里面的内容,如果在解析过程中没有出现报错,就说明解析成功了。

解析失败则会出现下面的报错:

在这里插入图片描述

通过 JWT 令牌技术就可以实现我们生活中的登录功能了。
在这里插入图片描述

相关推荐

  1. JWT令牌(Token)设计

    2024-03-11 07:00:05       35 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-11 07:00:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-11 07:00:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-11 07:00:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-11 07:00:05       18 阅读

热门阅读

  1. storm连接kafka

    2024-03-11 07:00:05       22 阅读
  2. 【ICRA】ICRA2023 Paper List

    2024-03-11 07:00:05       21 阅读
  3. leetcode热题HOT 25. K 个一组翻转链表

    2024-03-11 07:00:05       23 阅读
  4. 【MySQL】MySQL内外连接

    2024-03-11 07:00:05       22 阅读
  5. shell utils

    2024-03-11 07:00:05       21 阅读
  6. opencv人脸识别实战2:刷脸功能(PyCharm实现)

    2024-03-11 07:00:05       19 阅读
  7. Visual studio编译器报1个无法解析的外部命令

    2024-03-11 07:00:05       22 阅读
  8. react实现表格多条件搜索

    2024-03-11 07:00:05       24 阅读
  9. 【Vue】生命周期

    2024-03-11 07:00:05       15 阅读
  10. Ubuntu启用ROOT用户和配置SSH远程

    2024-03-11 07:00:05       22 阅读