JWT 单点登录探析:原理、用途与安全实践

JWT 单点登录探析:原理、用途与安全实践

什么是 JWT?

JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制

从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token。

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

JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。(JWT 存储在【客户端】)

并且,使用 JWT 认证可以有效避免 CSRF 攻击,因为 JWT 一般是存在在 localStorage 中,使用 JWT 进行身份验证的过程中是不会涉及到 Cookie 的。

JWT 有什么用?

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

JWT 的组成

JWT 由 3 部分组成,用 . 拼接

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJ1c2VybmFtZSI6IlRvbSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjMyMjM2NzUsImp0aSI6ImQ2MTJjZjcxLWI5ZmUtNGMwNy04MzQwLTViOWViZmMyNjExNyJ9
.FOS9Y7rYNdc2AOidnSPrgg2XTYePU0yGZ598h2gtabE

可以在 jwt.io 这个网站上对 JWT 进行解码,解码之后得到的就是 Header、Payload、Signature 这三部分。

这三部分分别是:

Header

Header 通常由两部分组成:

  • typ(Type):令牌类型,也就是 JWT。
  • alg(Algorithm):签名算法,比如 HS256。

JSON 形式的 Header 被转换成 Base64 编码,成为 JWT 的第一部分。

{
  'typ': 'JWT',
  'alg': 'HS256'
}

Payload 声明

Payload 也是 JSON 格式数据,其中包含了 Claims(声明,包含 JWT 的相关信息)。

Payload 部分默认是不加密的,一定不要将隐私信息存放在 Payload 当中!!!

JSON 形式的 Payload 被转换成 Base64 编码,成为 JWT 的第二部分

Claims 分为三种类型:

  • Registered Claims(注册声明):预定义的一些声明,建议使用,但不是强制性的。
  • Public Claims(公有声明):JWT 签发方可以自定义的声明,但是为了避免冲突,应该在 IANA JSON Web Token Registry 中定义它们。
  • Private Claims(私有声明):JWT 签发方因为项目需要而自定义的声明,更符合实际项目场景使用。

下面是一些常见的注册声明:

  • iss(issuer):JWT 签发方。
  • iat(issued at time):JWT 签发时间。
  • sub(subject):JWT 主题。
  • aud(audience):JWT 接收方。
  • exp(expiration time):JWT 的过期时间。
  • nbf(not before time):JWT 生效时间,早于该定义的时间的 JWT 不能被接受处理。
  • jti(JWT ID):JWT 唯一标识。
{
  "uid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a",
  "sub": "1234567890",
  "name": "John Doe",
  "exp": 15323232,
  "iat": 1516239022,
  "scope": ["admin", "user"]
}
{
    "sub": '1234567890',
    "name": 'john',
    "admin": true
}

Signature 签名

Signature 部分是对前两部分的签名,作用是防止 JWT(主要是 payload)被篡改

这个签名的生成需要用到:

  • Header + Payload。
  • 存放在服务端的密钥(一定不要泄露出去)。 – secret
  • 签名算法。 – HMACSHA256
// 第一种方式
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret');

// 第二种方式
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,这个字符串就是 JWT 。

相关依赖

pom.xml 文件

<dependency>
    <groupId>io.jsonwebtokengroupId>
    <artifactId>jjwtartifactId>
    <version>0.9.1version>
dependency>

<dependency>
    <groupId>javax.xml.bindgroupId>
    <artifactId>jaxb-apiartifactId>
    <version>2.3.0version>
dependency>
<dependency>
    <groupId>com.sun.xml.bindgroupId>
    <artifactId>jaxb-implartifactId>
    <version>2.3.0version>
dependency>
<dependency>
    <groupId>com.sun.xml.bindgroupId>
    <artifactId>jaxb-coreartifactId>
    <version>2.3.0version>
dependency>
<dependency>
    <groupId>javax.activationgroupId>
    <artifactId>activationartifactId>
    <version>1.1.1version>
dependency>

开源工具类使用

也可用 Hutool 中的工具类生成 JWT 来进行食用:JWT工具-JWTUtil (hutool.cn)

如何基于 JWT 进行身份验证?

在基于 JWT 进行身份验证的的应用程序中,

服务器通过 Payload、Header 和 Secret(密钥)创建 JWT 并将 JWT 发送给客户端。

客户端接收到 JWT 之后,会将其保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。

从用户的角度来分析:

  1. 用户向服务器发送用户名、密码以及验证码用于登陆系统。
  2. 如果用户用户名、密码以及验证码校验正确的话,服务端会返回已经签名的 Token,也就是 JWT。
  3. 用户以后每次向后端发请求都在 Header 中带上这个 JWT 。
  4. 服务端检查 JWT 并从中获取用户相关信息。

为什么用 JWT 代替 Session

JWT (JSON Web Token) 被用于代替传统的基于 Session 的身份认证和授权机制的主要原因有以下几点:

  1. 无需服务器存储: 在传统的 Session 机制中,服务器需要维护每个用户的会话状态,通常是存储在内存或数据库中。而使用 JWT,所有的用户信息和权限信息都被编码到了 token 中,服务器无需存储任何状态信息,这样可以减轻服务器的负担。
  2. 跨域支持: JWT 可以轻松地在多个域之间进行传递和使用,这使得跨域通信和微服务架构变得更加简单。
  3. 扩展性: JWT 是一种开放标准,可以支持自定义的声明和数据结构。这使得 JWT 非常灵活,可以根据实际需求添加额外的信息。
  4. 无状态性: 传统的基于 Session 的认证机制需要服务器保存会话状态,而 JWT 是无状态的。每个请求都包含了认证信息,服务器不需要再去查询数据库或存储中心来验证用户的身份。
  5. 安全性: JWT 内置了签名机制,可以验证 token 的真实性和完整性。这样即使 token 被篡改,服务器也能识别出来并拒绝该 token。
  6. 适用于分布式系统: JWT 适用于分布式系统和微服务架构,可以跨多个服务进行认证和授权。

总的来说,JWT 是一种轻量级、可扩展、无状态和安全的认证机制,相较于传统的基于 Session 的认证机制,JWT 具有更多优势,特别适合于现代的分布式、跨域和无状态的应用场景。

单点登录

两种单点登录实现方案

  1. redis + token
    • token 是指一个无意义的,随机的字符串
    • 后端校验用户名密码之后,生成 token 后放入 redis
    • 前端将 token 放入 header(利用 store
    • 其他页面请求校验时,从 header 获取 token,然后根据 token 到 redis 获取数据进行判断,有数据则登录校验成功。(主要是看 token 是否已失效)
  2. jwt
    • jwt 生成的 token 是有意义的
    • 使用工具包来校验 token

操作细节

1、前端在用户登录之后保存登录信息

          // 变量提交:保存登录信息, 即 token
          store.commit("setMember", data.content);

2、修改 axios 全局拦截器

为请求 headers 增加 token,并返回配置

axios.interceptors.request.use(function (config) {
  console.log('请求参数:', config);
  const _token = store.state.member.token;
  if (_token) {
    config.headers.token = _token;
    console.log("请求headers增加token:", _token);
  }
  return config;
}, error => {
  return Promise.reject(error);
});

JWT 单点登录原理与存在的问题及解决方案

Hutool 对 JWT 的介绍:概述 (hutool.cn)

存在的问题分析:

  1. token 被解密

    • 加盐值(密钥),每个项目的盐值不能一样,避免一个项目被破解,全部项目都被破解
  2. token 被拿到第三方使用

    • 简单来说就是: 自己的产品,被别人包了一个界面,做成他们收费的产品。(比如 ChatGPT 聊天机器人,外表包装成一个小程序,实际上是利用开发者设置的 token 去官网请求信息)

    • 没啥好办法,只能使用限流(检测流量大的情况)

学习参考

  • JWT 基础概念详解
  • 用户认证:基于jwt和session的区别和优缺点

你可能感兴趣的:(系统设计,安全,jwt)