承接上文的笔记继续分享啦。
本文为原创,未经同意请勿转载
由于篇幅有点长,所以笔者将我关于这部分的笔记分为四个篇章(文章开头后面附录上下篇链接),避免读者的阅读疲倦感,同时也方便大家的阅读啦。如果下面笔记中存在错误,欢迎大家及时指正,学习与讨论。
虽然Session把用户状态和行为信息放在服务器上,可以提高一定的安全性,但Session ID保存在Cookie中,也有被窃取的可能,跟Cookie一样,Session也会受到CSRF的攻击,而且Session请求的体积比较大,也会增加服务器的成本(需要考虑分布式和服务器负载均衡的问题)。因此,Token机制采用打包加证件的方式来保证安全和信息的轻量化。
也就是Token机制将信息打包到Cookie中,这种样子可以减少服务器压力,同时Token自己作为访问资源的凭证(客户端唯一的标识),可以用于身份验证和授权。有点类似Cookie, 但更加安全。
Token是服务器将验证后的登陆凭证做数字签名,加密之后得到的字符串,一般由用户唯一身份标识,当前时间戳,签名(防止第三方恶意拼接)和可选的固定参数(固定参数是为了避免多次查库)加密组成,是无状态的,用于表示用户或客户端的身份和权限。
在Web开发中,Token一般由服务器生成,并在返回给客户端前进行加密和签名操作,以确保其安全性。客户端收到Token后,会将其存储,并在后续请求附带在请求头中发送给服务器,以便服务器能够验证客户端的身份和权限信息。
针对上述对Token的定义,笔者我进行了相关的思考,主要是下面几个点。下面笔者我也将自己思考的结果进行了相关整理。当然笔者的对其理解也存在局限性,下面观点的思考无法保证完全的正确性,希望大家带着辨别的角度来进行阅读。如果有笔者理解错的,也十分欢迎大家及时指正笔者我关于这方面的理解与看法!
如何理解Token是无状态的? 这里的无状态是指什么?
这里先略微的说一下:Token并不一定都是无状态的! 只不过我们平常说的Token更多偏向于说的是JWT,而JWT标准的访问令牌Token通常被设计成无状态的,所以并不是所有的Token都是无状态的,需要根据Token类型和场景来做判断。比如,Session Token和基于OAuth2.0的授权码。Session Token通常是服务器端生成的一段字符串,用于标识用户会话信息。服务器会将该Token存储在缓存或数据库中,以便在用户请求时验证其有效性。由于Token存储在服务器上,因此可以视为具有状态。
Token无状态的定义
Token无状态属性是指后端服务的状态,而非HTTP协议本身的状态。所以这里的无状态定义与HTTP无状态的属性定义是不一样的!
那什么是后端服务的状态呢?
后端服务的状态根据两个来自相同发起者的请求在服务器端是否具有上下文关系分为两种:有状态服务和无状态服务。
1. 无状态服务(stateless service):对单次请求的处理,不依赖其他请求,也就是说,处理一次请求所需的全部信息,要么都包含在这个请求里,要么可以从外部获取到(比如说数据库),服务器本身不存储任何信息 。
2. 有状态服务(stateful service): 则相反,它会在自身保存一些数据,先后的请求是有关联的 。
3. 简单来说就是: 有状态就是把一个通信分多次传输,后面的通信会受制于前面的通信,而无状态就是一次全给他也不管他能不能接收,前后通信无关联。
Token无状态
之所以说Token是无状态,是因为Token是包含有关用户身份和权限信息的字符串,所有的状态信息附加在Token上,服务端并可以不保存Token的相关信息,即不会保存关于客户端的任何状态信息,也就是说在每次请求中都需要提供Token并由服务器进行验证,而不是依赖服务器缓存存储的信息来验证权限。
简单来说,Token的无状态属性是相对服务端而言的。对于服务端来说,它并不保存任何关于Token的信息,只是根据Token所携带的信息来做出响应。
Token不是为了解决HTTP无状态吗?那岂不是矛盾了?那无状态是怎么解决的?
根据上面的介绍,我们知道Token的无状态特性与HTTP无状态特性不能等同,它们之间是无关的。因此,Token确实是为了解决HTTP无状态而产生的一种解决方案,但在使用过程中并不会与Token的无状态属性矛盾。Token的作用是提供一个短暂的标识符,通过在发送请求时携带该标识符,并通过服务器对Token的验证来获取客户端的状态信息。这种利用客户端的存储机制可以在无状态的HTTP协议上实现“记住”客户端身份和许可状态,而不需要服务器端存储大量的用户信息,从而提高了Web应用程序的可扩展性和性能。简单来说,Token通过在客户端和服务器之间传递信息来维护通信状态。
这里推荐一个知乎答主对无状态的解释,感觉比较专业:HTTP是一个无状态的协议。这句话里的无状态是什么意思? - 灵剑的回答 - 知乎
那Cookie和Session是不是无状态的?
与Token的无状态不同,Cookie - Session 模型是常见的有状态模型。根据前面对无状态的定义及Session,Cookie的介绍,我们知道Session会在服务端维持一个状态列表信息,因此,Session是有状态的。当然这里的有状态定义与通信协议本身的有状态定义并无关联。当然单独的Cookie跟Token的设置可以是无状态也可以是有状态,但现在一般Cookie都会与SessionID结合适用,所以一般来说Cookie也就是我们上面所说的有状态。
✍ 服务状态与HTTP协议状态无关
HTTP协议是无状态的协议,这个其实跟服务的状态是无关的。一个服务不管使用何种协议,都可以在服务层面上是有状态的,因为这和通信协议无关,只需要它在响应请求时改变自己的状态即可。所以说,服务本身有没有状态、支不支持会话,其实跟HTTP协议是否有状态是无关的。
为什么可以避免重复查库?(这里主要是指JWT,不是传统的Token)
这是因为,在获取 Token 时,服务器只需要验证 Token 的合法性和有效性,而不需要每次访问都查询数据库。在基于Session的身份认证中,服务器需要查询数据库来获取用户的信息(前面我们根据Session ID来查库),以便验证用户的身份。而 Token 则包含用户的信息(其实这里就是在内存检索缓存的结果,Token保存在客户端),不需要进行数据库的查询,也就说服务器只需要验证 Token 的合法性和有效性,就可以知道用户是否登录成功。这样就可以避免重复查询数据库,减少系统的负载,进而提高系统的性能和响应速度。
(1)由服务器生成并且保存在客户端的加密后的随机字符串——》随机性,不可预测性:与Cookie和Session以键值对的形式表示,Token通常采用加密后的随机字符串来表示,也就是我们常说的“令牌”。由于Token值得生成和加密过程都是基于随机数的,所以Token具有随机性和不可预测性,这也使得Token相对Cookie和Session更加安全。
(2)可以存储如JSON格式、XML格式、Base64编码等各种类型的数据:Session确实可以存储任意类型的数据,包括字符串、数字、对象等。而Cookie只能存储字符串类型的数据,对于复杂类型的数据,例如对象和数组,需要使用JSON.stringify()方法将其转换为字符串后再存储,在读取Cookie时需要使用JSON.parse()方法进行解析。与Cookie类似,Token也可以存储各种类型的数据,例如JSON格式、XML格式、Base64编码等等。这些格式的数据也是以字符串形式保存在Token中(Token本身就是字符串)。
(3)无需在服务器记录会话状态——》独立性,无状态:根据前面两章的介绍,我们知道Session和Cookie的创建和维护需要由服务器负责,在服务端需要记录SessionID等信息,有状态的列表信息。而Token则不需要再服务端记录会话状态,因为Token 自身包含了所有登录用户的信息,只需要在客户端的Cookie或本地介质存储状态信息。所以Token具有我们前面所提到的无状态属性。
(4)具有时效性:Token有一定的有效期,在过期前需要重新获取或续期,否则将无法再次进行身份验证。一般情况下,Token的有效期会设置为一个较短的时间段来减少被攻击者截获并滥用的风险。
(5)安全性高:相对于Session和Cookie,Token更加安全。因为Session和Cookie可能被盗取或篡改,从而导致用户信息泄露和身份认证失效。而使用Token时,服务器会根据用户提交的Token来验证用户的身份以及权限,不需要保存大量的敏感信息在服务端,因此更加安全。同时,Token通过加密处理,无法直接修改其中的信息,从而保证了传输过程中的安全性。
(6)去中心化(也叫去耦):Token的去中心化指的是它不依靠中心机构来管理和发行,可以在任何地方生成,只要在你的API被调用的时候,你便可以进行Token生成调用。
(7)支持跨域,跨平台访问,具有很好的扩展性:Session和Cookie只能在HTTP请求/响应中进行传输,受到协议限制,难以跨域使用。而Token完全由应用管理,所以它可以避开同源策略,可以跨平台、跨域名、跨设备使用,具有更好的扩展性。
(8)具有唯一性:通常情况下,Token 是用伪随机算法(如 UUID 算法)生成的随机值。由于该算法采用了一些不可预测的输入因素,如当前时间、MAC 地址、随机数等,使得每次生成的 Token 都是独一无二的,极大程度上保证了它们的唯一性和安全性。
/page?token=ABC123
。这样,客户端在访问这个 URL 时,需要同时将 Token 发送回服务器,以便服务器能够验证其合法性和有效性。
url传输Token的主要危害: 会暴露token值。URL携带token是不安全的,会暴露Token值,Token可以放在请求header的Authorization中,在https下传输,保证Token传输的安全。 Token应该在服务端生成,密钥也在服务端,校验在服务端,这是起码的安全,另外Token也应该有时效性。
Token 可以放在 Cookie ,但在功能上的话不推荐。Token本身提出来就是为了防止CSRF(跨站请求伪造)问题,但把token放在cookie中,则Token还是会随Cookie自动携带至请求中,防止不了CSRF攻击。容易产生CSRF(跨站请求伪造)问题,Token一般存储在sessionStorage/localStorage里面。token的出现就是为了解决用户登录后的鉴权问题,如果采用Cookie+Session的鉴权方式,则无法有效地防止CSRF攻击,同时,如果服务端采用负载均衡策略进行分布式架构,Session也会存在一致性问题,需要额外的开销维护Session一致性。
根据定义,我们可知道,Token由uid+time+sign[+可选参数]组成。
优点总结
:随机性与独立性,安全系数高,支持跨域跨平台跨语言,扩展性好,无状态可减少服务端压力,适用于移动端应用和分布式/微服务等,去中心化便于管理与调试,统一性便于实现资源共享。优点展开
:
Authorization
字段中自动携带,在请求被发送之前会先进行身份鉴别,只有在服务端验证通过后才会返回相应的资源或操作,这样可以有效地避免 CSRF 攻击。Authorization
是一个HTTP安全请求首部,用于表示客户端向服务器进行身份认证的凭据信息,其格式一般为type value
,其中 type
表示身份认证类型或方案,而 value
则表示对应的凭据值。常见的身份认证方式包括基本身份验证(Basic Authentication)、Bearer Token 身份验证和摘要身份验证(Digest Authentication)等。例如,当使用 Bearer Token 方式进行身份验证时,可以在 Authorization
字段中设置如下内容:Authorization: Bearer <Token_value>
思考2:Token可以百分百防止CSRF攻击?Access-Control-Allow-Credentials: true
字段,以明确告诉浏览器允许该跨域资源可以携带 Cookie 进行通信,并且还需要在 XMLHttpRequest 对象中将 XMLHttpRequest.withCredentials
设置为true。缺点总结
:虽然Token是轻量级的,但是通常会包含大量的元数据信息,从而导致性能下降效率低,而且Token进行认证需要依赖认证服务器,存在过期问题,和不安全性。缺点展开
:
到此为止,我们前面讲的Token应用都是身份标识之类的作用,而且Token作为我们开发软件进行身份标识也是十分常见的,相信大家也都比较熟悉。但Token除了身份标识还有没有其他功能呢?答案当然是肯定的。
在我面试的时候回答了有关Cookie,Session和Token的理解时,面试官也给了我一些知识的分享。因为我讲的主要是在身份认证这一主线上的发展。面试官说到的是他自己更加偏向把Token单独拎出来。为什么呢?因为Token比起Cookie和Session的话,它其实更多的是一种数字和密码的凭证…讲了很多Token在实际业务场景中的应用,感觉还是非常受用的。后面我也阅读了相关的文献结合面试官的分享整理了下面一些常见的应用与用途。美国对Token的定义有三类,第一类是货币型;第二类是应用型;第三类是证券型。从这三种类型,我们也可以知道Token的应用还有哪些了。
下面是Token的一些常见应用和用途:
在软件设计与开发方面,Token机制常见的应用场景:
这里笔者我在阅读相关Token类型的文献时,看到一个挺不错的图:
来源链接:基于 token 的多平台身份认证架构设计
密码层:最传统的用户和系统之间约定的数字身份认证方式。
会话层:用户登录后的会话生命周期的会话认证。
调用层:用户在会话期间对应用程序接口的调用认证。
应用层:用户获取了接口访问调用权限后的一些场景或者身份认证应用。
通过上面的知识,我们也对Token有了一个大概的了解,所以在终篇中,笔者我将主要介绍一下主要介绍JWT相关要点并总结JWT和Token的区别,相关的加密算法及网络安全问题以及总结Cookie到Token的区别与发展。
笔记链接:【前端知识】Cookie, Session,Token和JWT的发展及区别(四)
码字不易,可能当中存在某些字体错误(笔者我没有发现),如果有错误,欢迎大家指正。