JWT认证

概念

JWT(JSON Web Token)是一种用于认证和授权的开放标准;是一个经过加密的,包含用户信息的且具有时效性的固定格式字符串,通常用于在不同的系统之间安全地传递声明(claims)。

JWT 由三部分组成:

  1. 头部(Header):头部通常包含了两部分信息,令牌的类型(JWT)和所使用的签名算法(例如 HMAC SHA256 或 RSA)。

  2. 载荷(Payload):载荷包含了一系列声明(claims)。声明是关于实体(通常是用户)和其他数据的声明。有三种类型的声明:

    • 注册声明(Registered Claims):这些是一组预定义的声明,包括 iss(签发者)、sub(主题)、aud(受众)、exp(到期时间)、nbf(生效时间)和iat(签发时间)等。
    • 私有声明(Private Claims):这些声明是应用程序特定的,通常用于在双方之间共享信息。
    • 公共声明(Public Claims):这些声明是可选的,可以按需要添加到令牌中。
  3. 签名(Signature):签名用于验证令牌的真实性和完整性。它是通过将头部和载荷进行签名生成的,使用密钥来生成签名。验证方可以使用相同的密钥来验证签名,以确保令牌内容未被篡改。

    按照下面的公式产生签名:

    1
    2
    3
    4
    HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret)

    整个JWT的头部、载荷、签名都会用Base64URL算法转成加密字符串

认证流程

在分布式架构下,利用网关和认证中心来实现JWT认证通常涉及多个服务和组件,以确保对应用程序的安全访问和身份验证。下面是两种方式的认证流程示例:

统一校验方式:

image-20230923155754228
  1. 用户登录
    • 用户通过网关发送登录请求,提供用户名和密码。
    • 网关将登录请求转发给认证中心。
  2. 认证中心验证用户
    • 认证中心验证用户提供的凭据(用户名和密码)是否有效。
    • 如果凭据有效,认证中心将创建一个JWT令牌,并在令牌的载荷中包含用户的信息(例如用户ID、角色等)。
    • 认证中心使用私钥对JWT令牌进行签名。
  3. JWT令牌生成
    • 认证中心生成JWT令牌,将其签名后返回给网关。
  4. 网关接收JWT令牌
    • 网关接收到JWT令牌,并在将其发送到客户端之前,可以选择对令牌进行一些处理,如添加HTTP头信息。
  5. 客户端存储JWT令牌
    • 客户端(通常是前端应用程序)收到JWT令牌后,将其存储在安全的地方,例如浏览器的本地存储或HTTP Cookie中。
  6. 后续请求
    • 客户端在后续请求中将JWT令牌包含在HTTP请求的头部中(通常是Authorization头),以证明其身份。
  7. 网关验证JWT令牌
    • 网关接收到后续请求,并从请求头中提取JWT令牌。
    • 网关使用与认证中心共享的公钥来验证JWT令牌的签名。
    • 如果签名验证成功,网关允许请求通过,并将请求转发给适当的微服务。
  8. 微服务处理请求
    • 微服务接收到请求后,可以从JWT令牌中提取用户信息,以确定请求的上下文和权限。
    • 微服务根据令牌中的信息来处理请求,执行相应的业务逻辑。

应用认证方式:

image-20230923160243730

  1. 用户登录
    • 用户通过网关发送登录请求,提供用户名和密码。
  2. 网关将请求转发给应用程序
    • 网关将登录请求转发给特定的应用程序微服务,而不对JWT进行验证或处理。
  3. 应用程序处理登录请求
    • 应用程序微服务接收到登录请求,将用户名和密码传递给认证中心。
  4. 认证中心验证用户
    • 认证中心验证用户提供的凭据(用户名和密码)是否有效。
    • 如果凭据有效,认证中心将创建一个JWT令牌,并在令牌的载荷中包含用户的信息(例如用户ID、角色等)。
    • 认证中心使用私钥对JWT令牌进行签名。
  5. JWT令牌生成
    • 认证中心生成JWT令牌,将其签名后返回给应用程序微服务。
  6. 应用程序微服务将JWT令牌返回给网关
    • 应用程序微服务将生成的JWT令牌返回给网关。
  7. 网关接收JWT令牌并将其传递到客户端
    • 网关接收到JWT令牌,并将其传递到客户端,通常存储在客户端的本地存储中。
  8. 后续请求
    • 客户端在后续请求中将JWT令牌包含在HTTP请求的头部中(通常是Authorization头),以证明其身份。
  9. 网关将请求转发给应用程序
    • 网关将包含JWT令牌的请求转发给特定的应用程序微服务。
  10. 应用程序微服务向认证中心验证JWT令牌
    • 应用程序微服务接收到请求后,从请求中提取JWT令牌,并将令牌发送到认证中心进行验证获取用户与权限数据。

区别:

  • 方案一:JWT校验无感知,验签过程无侵入执行效率低,适用于低并发企业级应用

  • 方案二:控制更加灵活,有一定代码侵入,代码可以灵活控制,适用于追求性能互联网应用

续签

JWT通常具有一个预定的生命周期(通常以秒为单位),一旦JWT到期,就需要续签或重新生成新的JWT。续签JWT的方法取决于你的使用情境和安全需求,

以下是一些常见的JWT续签方案:

  1. 基于Redis的续签方案

    image-20230925102732404

    • 在登录认证之后需要在redis中存储key为用户信息的缓存并设置过期时间(这个过期时间就是JWT最大存活时间);
    • 再次进行登录校验的时候,就需要去redis中获取数据判断是否有效:
      • 不存在该key则直接拒绝访问(即长时间不操作)
      • 存在判断有效期:
        • 超过一个小时不做任何处理正常响应
        • 不足一小时:对redis中有效期进行延长

    缺点:JWT从无状态变为了有状态

  2. 刷新令牌方案

    image-20230925104104832

    使用刷新令牌是一种常见的JWT续签方法。用户在登录时会获得一个JWT和一个刷新令牌。JWT用于短期访问,而刷新令牌用于获取新的JWT。当JWT过期时,客户端可以使用刷新令牌向服务器请求新的JWT,服务器会验证刷新令牌的有效性,如果有效,则签发一个新的JWT。

    为什么必须要两个refresh_token?为什么不直接设置token一个小时过期,判断还有10分钟过期的时候,生成新的token进行替换?

    refresh_token和access_token的设计最根本的原因是因为 token验证服务器 和 token分发服务器 是分离的,换句话说 refresh_token 从来没有发送到真正执行验证的业务服务器上面过 这样保证了安全性(流程是access_token去业务服务区上验证能不能用,不能用那么客户端拿着refresh_token到真正的token分发服务器上换取新的 access_token 然后再去请求业务服务器)