温故知新,.Net Core遇见JWT(JSON Web Token)授权机制方案

什么是JWT

JWT (JSON Web Token) 是一个开放标准,它定义了一种以紧凑和自包含的方法,用于在双方之间安全地传输编码为JSON对象的信息。

因此,简单来说,它是JSON格式的加密字符串,其中包含敏感信息,它使我们能够验证不同服务间的发送者。

应该在什么时候使用JWT?

  • 授权: 这是使用JWT最常见的场景。JWT用于授权而非身份验证。通过身份验证,我们验证用户名和密码是否有效,并将用户登录到系统中。通过授权,我们可以验证发送到服务器的请求是否属于通过身份验证登录的用户,从而可以授予该用户访问系统的权限,继而批准该用户使用获得的Token访问路由、服务和资源。

  • 信息交换JSON Web Token是在双方之间安全地传输信息的一种好方法。因为JWT可以被签名(例如,使用公钥/私钥对),所以使您能确保发送方是他们所声称的那一方。此外,由于签名是使用HeaderPayload计算的,因此还使您能验证发送的内容没有被篡改。

在计算机科学与电信领域,负载(英语:Payload)是数据传输中所欲传输的实际信息,通常也被称作实际数据或者数据体。信头与元数据,或称为开销数据,仅用于辅助数据传输。在计算机病毒或电脑蠕虫领域中,负载指的是进行有害操作的部分,例如:数据销毁、发送垃圾邮件等。

JWT与Session Id比较

小型Web应用程序

1) Session Id实现

在传统的Web应用程序中,我们使用Session来授权用户,当用户登录到应用程序后,我们会为该用户分配一个唯一的Session Id。我们将此Session Id保存在用户浏览器的安全Cookie中和服务器的内存中。我们对每个请求都使用相同的会话,以便服务器知道该用户已通过身份验证。对于每个请求,Cookie中的Session Id都会与服务器内存中的Session Id作匹配,以验证用户是否被授权。

Session Id描述了用于识别HTTP会话的统一资源标识符。会话标识UIS允许在有限域内链接HTTP交易。这为商业服务器对人口数据收集的需求和用户的隐私问题提供了平衡。此外,会话识别UIS可用作高安全性认证机制的一部分,以防止重播攻击。

Cookie,有时也用其复数形式Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息。

2) JWT实现

JWT实现中,我们使用JWT授权用户,当用户登录到应用程序后,就会为每个通过身份验证的用户生成一个唯一的JWT。我们将该token保存在浏览器的Local Storage或者Cookie中,而不会在服务器端保存任何内容。对于每个请求,该Token都会被发送到服务器进行解密和验证,以核实该用户是否已授权,不管以何种方式篡改了Token都会被拒绝。

网络存储(Web Local Storage)可以被看作为改进的Cookie,提供更大的存储容量(在谷歌浏览器2.5 MB每网域。在Mozilla FirefoxOpera中为5 MB每网域。在Internet Explorer中则为10 MB 每存储区域)和更好的编程接口。

Cookie可以被客户端和服务器访问,但网络存储只限被客户端脚本(Client-SideScripts,例如JavaScript)控制。网络存储的资料并不会在每个HTTP请求下发送到服务器,网络服务器亦不能直接把资料直接写入到网络存储,但是当然可以发出读取和写入请求。

3) 总结

这种实现对于小型站点来说很好,仅仅因为我们不再存储Session Id,从而通过减少服务器的负载,我们已经从JWT中看到了一些好处。

高级Web应用程序(多个服务器)

如果我们的应用程序越来越受欢迎,需要我们对其进行扩展,会发生什么呢?

1) Session Id实现

我们需要有一台连接到负载均衡器的新服务器,以便基于流量和可用性在Web服务器之间导航流量。这种实现给我们带来了一个新的问题,如下所示:

如果用户1登录到了服务器1,那么服务器1已经将Session保存在其内存中,当用户1发出另一个请求并且负载均衡器将该请求重定向到了服务器2,而服务器2没有保存该Session信息,这时会发生什么情况?

用户将被认为已退出应用程序并被要求再次登录,这不是一个好的用户体验。通常,我们解决这个问题的方法是引入缓存:

现在,所有的Session也将同时保存在缓存中,因此任何一台服务器都可以检查该Session是否存在,并可以利用它来验证用户并授予他们对应用程序的访问权限。

尽管缓存解决了我们的问题,但是在生产环境中,这种解决方案有着昂贵的成本:

  • 需要大量的RAM、CPU、存储来跟踪所有这些会话和平稳地处理请求
  • 需要维护缓存,以确保没有幽灵会话或无效会话
  • 万一某台服务器崩溃,所有未与缓存同步的会话都会丢失
  • 使用户无效更复杂
  • 托管成本高

2) JWT实现

让我们来看看如何通过JWT实现来处理相同的情况。

不同于在Cookie中使用Session Id与服务器内存中的Session作匹配;我们可以使用JWT来代替它。此时,当用户登录到我们的应用程序时,服务器将不会生成Session Id并将其保存在内存中,而是会创建一个JWT Token,并对其进行编码和序列化,然后使用自己的加密机制对其进行签名。通过这种方式,服务将知道一旦对它做了变更或篡改,便将其变为无效。由于通过服务器的加密机制对其进行了签名,所以这是可以被检验的。

3) 总结

使用JWT可以更容易地管理可伸缩性,因为我们不需要服务器来处理任何会话检查或缓存检查。请求可以转发到负载均衡器为其分配的任一服务器,而无需担心会话的可用性。万一某台服务器宕机,所有的Token将仍然有效,因为所有服务器上的加密机制是一样的。

JWT和Session Id的区别总结

让我们来快速总结一下JWTSession Id的区别

1) JWT

  • 服务器上不保存任何东西,JWT存储于客户端中
  • 由服务器加密和签名
  • Token包含用户的所有信息
  • 所有信息都存储于Token本身中
  • 易于缩放

2) Session Id

  • Session Id保存于服务器和客户端中
  • 加密并签名
  • Session Id是对用户的引用
  • 服务器需要查找用户并进行必要的检查
  • 难以缩放

JWT结构

JSON Web Token由三部分组成,以点(.)分隔,分别是:

  • Header(标头)
  • Payload(有效负载)
  • Signature(签名)

因此,JWT通常如下所示:

xxxxxx.yyyyyyy.zzzzzzzz

这种分隔使从视觉上更容易看出Token的不同部分。让我们来分解一下它的不同的部分。

Header通常由两部分组成:

  • Token的类型,这里是JWT
  • 使用的签名算法,比如HMACSHA256RSA

例如:

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

然后,将此JSONBase64Url编码,形成JWT的第一部分。

Payload(Data)

Token的第二部分是有效负载,其中包含Claims(声明),Claims是有关实体(通常是用户)和其他数据的声明。

有三种类型的Claimsregisteredpublicprivate claims

  • Registered Claims:这是一组非强制性的但建议使用的预定义Claims,以提供一组有用的、可互操作的Claims。其中包含:iss(issuer,签发者)、exp(expirationtime,过期时间)、sub(subject,主题)、aud(audience,受众)等等。
  • Public Claims:这些可以由使用JWT的人员随意定义。但是为了避免冲突,应该在IANA JSON Web Token Registry中定义它们,或者将其定义为包含抗冲突命名空间的URI
  • Private Claims:这些是自定义声明,是为了在同意使用它们的双方共享信息而创建的,它们既不是注册的声明,也不是公共的声明。

举一个有效负载的例子:

{
    "sub": "221122112",
    "name": "Mohamd Lawand",
    "admin": true,
    "exp": 15323232,
    "iat": 14567766 // 该Token的签发时间
}

然后,对有效负载进行Base64Url编码,形成JSON Web Token的第二部分。

除非将其加密,否则请不要将机密信息放入JWTPayloadHeader元素中。

签名

签名使我们能够验证Token是否有效和没被篡改。它的工作方式是获取Token的前两部分,将HeaderPayload分别编码为Base64,然后将它们用“.”连接起来。这样我们就拥有了与用户共享的所有数据。

然后,获取在第一部分(Header)中提供的算法并应用于上面的连接结果。如果前两部分的哈希结果与token的第三部分匹配,则表示此JWT是有效的;如果不匹配,则表示此Token被修改过,是无效的。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

这种方案的唯一威胁是密钥在服务器以外的任何地方都可用。但是,如果我们保护好密钥的安全,就没有什么能损害这一过程。

签名被用于验证消息在传输过程中没有被篡改,而且,当Token是使用私钥签名时,它还可以验证JWT发送方的真实身份。

它的工作原理与密码哈希非常相似——我们将两部分组合在一起,并且使用特定的算法进行单向哈希,然后我们比较哈希的结果看它们是否有效。

1) 签名方式

现在,让我们来看一下对JWT进行签名的方式:

  • 一个Secret(使用HMAC算法)
  • 一个公钥/私钥对(使用RSAECDSA

签名的Token可以验证其中包含的Claims的完整性,而加密的Token则可以向其他方隐藏这些Claims

参考

posted @ 2021-04-29 23:18  TaylorShi  阅读(489)  评论(0编辑  收藏  举报