`

深入了解Token认证的来龙去脉

阅读更多

 

Token 是在服务端产生的,如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。

 

 

不久前,我在前后端分离实践中提到了基于 Token 的认证,现在我们稍稍深入一些。

 

通常情况下,我们在讨论某个技术的时候,都是从问题开始。那么第一个问题:

 

 

为什么要用 Token?

 

要回答这个问题很简单,因为它能解决问题!

 

Token 能解决哪些问题呢?有如下几点:

  • Token 完全由应用管理,所以它可以避开同源策略。

  • Token 可以避免 CSRF 攻击(http://dwz.cn/7joLzx)。

  • Token 可以是无状态的,可以在多个服务间共享。

 

Token 是在服务端产生的,如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。

 

前端可以在每次请求的时候带上 Token 证明自己的合法地位。如果这个 Token 在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。

 

于是,又一个问题产生了:需要为 Token 设置有效期吗?

 

 

需要设置有效期吗?

 

对于这个问题,我们不妨先看两个例子:

  • 登录密码,一般要求定期改变密码,以防止泄漏,所以密码是有有效期的。

  • 安全证书,SSL 安全证书都有有效期,目的是为了解决吊销的问题,对于这个问题的详细情况,来看看知乎的回答(http://dwz.cn/7joMhq)。

 

所以无论是从安全的角度考虑,还是从吊销的角度考虑,Token 都需要设有效期。

 

那么有效期多长合适呢?只能说,根据系统的安全需要,尽可能的短,但也不能短得离谱。

 

想像一下手机的自动熄屏时间,如果设置为 10 秒钟无操作自动熄屏,再次点亮需要输入密码,会不会疯?

 

如果你觉得不会,那就亲自试一试,设置成可以设置的最短时间,坚持一周就好(不排除有人适应这个时间,毕竟手机厂商也是有用户体验研究的)。

 

然后新问题产生了,如果用户在正常操作的过程中,Token 过期失效了,要求用户重新登录……用户体验岂不是很糟糕?

 

为了解决在操作过程不能让用户感到 Token 失效这个问题,有一种方案是在服务器端保存 Token 状态,用户每次操作都会自动刷新(推迟) Token 的过期时间——Session 就是采用这种策略来保持用户登录状态的。

 

然而仍然存在这样一个问题,在前后端分离、单页 App 这些情况下,每秒种可能发起很多次请求,每次都去刷新过期时间会产生非常大的代价。

 

如果 Token 的过期时间被持久化到数据库或文件,代价就更大了。所以通常为了提升效率,减少消耗,会把 Token 的过期时保存在缓存或者内存中。

 

还有另一种方案,使用 Refresh Token,它可以避免频繁的读写操作。这种方案中,服务端不需要刷新 Token 的过期时间,一旦 Token 过期,就反馈给前端,前端使用 Refresh Token 申请一个全新 Token 继续使用。

 

这种方案中,服务端只需要在客户端请求更新 Token 的时候对 Refresh Token 的有效性进行一次检查,大大减少了更新有效期的操作,也就避免了频繁读写。

 

当然 Refresh Token 也是有有效期的,但是这个有效期就可以长一点了,比如,以天为单位的时间。

 

 

时序图表示

 

使用 Token 和 Refresh Token 的时序图如下:

 

 

登录

 

 

业务请求

 

 

Token 过期,刷新 Token

 

 

上面的时序图中并未提到 Refresh Token 过期怎么办。不过很显然,Refresh Token 既然已经过期,就该要求用户重新登录了。

 

当然还可以把这个机制设计得更复杂一些,比如,Refresh Token 每次使用的时候,都更新它的过期时间,直到与它的创建时间相比,已经超过了非常长的一段时间(比如三个月),这等于是在相当长一段时间内允许 Refresh Token 自动续期。

 

到目前为止,Token 都是有状态的,即在服务端需要保存并记录相关属性。那说好的无状态呢,怎么实现?

 

 

无状态 Token

 

如果我们把所有状态信息都附加在 Token 上,服务器就可以不保存。但是服务端仍然需要认证 Token 有效。

 

不过只要服务端能确认是自己签发的 Token,而且其信息未被改动过,那就可以认为 Token 有效——“签名”可以作此保证。

 

平时常说的签名都存在一方签发,另一方验证的情况,所以要使用非对称加密算法。

 

但是在这里,签发和验证都是同一方,所以对称加密算法就能达到要求,而对称算法比非对称算法要快得多(可达数十倍差距)。

 

更进一步思考,对称加密算法除了加密,还带有还原加密内容的功能,而这一功能在对 Token 签名时并无必要——既然不需要解密,摘要(散列)算法就会更快。你可以指定密码的散列算法,自然是 HMAC。

 

上面说了这么多,还需要自己去实现吗?不用!JWT 已经定义了详细的规范,而且有各种语言的若干实现。

 

不过在使用无状态 Token 的时候在服务端会有一些变化,服务端虽然不保存有效的 Token 了,却需要保存未到期却已注销的 Token。

 

如果一个 Token 未到期就被用户主动注销,那么服务器需要保存这个被注销的 Token,以便下次收到使用这个仍在有效期内的 Token 时判其无效。有没有感到一点沮丧?

 

在前端可控的情况下(比如前端和服务端在同一个项目组内),可以协商:前端一旦注销成功,就丢掉本地保存(比如保存在内存、Local Storage 等)的 Token 和 Refresh Token。

 

基于这样的约定,服务器就可以假设收到的 Token 一定是没注销的(因为注销之后前端就不会再使用了)。

 

如果前端不可控的情况,仍然可以进行上面的假设,但是这种情况下,需要尽量缩短 Token 的有效期,而且必须在用户主动注销的情况下让 Refresh Token 无效。

 

这个操作存在一定的安全漏洞,因为用户会认为已经注销了,实际上在较短的一段时间内并没有注销。如果应用设计中,这点漏洞并不会造成什么损失,那采用这种策略就是可行的。

 

在使用无状态 Token 的时候,有两点需要注意:

  • Refresh Token 有效时间较长,所以它应该在服务器端有状态,以增强安全性,确保用户注销时可控。

  • 应该考虑使用二次认证来增强敏感操作的安全性。

 

到此,关于 Token 的话题似乎差不多了——然而并没有,上面说的只是认证服务和业务服务集成在一起的情况,如果是分离的情况呢?

 

 

分离认证服务

 

当 Token 无状态之后,单点登录就变得容易了。前端拿到一个有效的 Token,它就可以在任何同一体系的服务上认证通过,只要它们使用同样的密钥和算法来认证 Token 的有效性。就像这样,如下图:

 

当然,如果 Token 过期了,前端仍然需要去认证服务更新 Token:

 

可见,虽然认证和业务分离了,实际却并没产生多大的差异。当然,这是建立在认证服务器信任业务服务器的前提下,因为认证服务器产生 Token 的密钥和业务服务器认证 Token 的密钥和算法相同。

 

换句话说,业务服务器同样可以创建有效的 Token。如果业务服务器不能被信任,该怎么办?

 

 

不受信的业务服务器

 

遇到不受信的业务服务器时,很容易想到的办法是使用不同的密钥。认证服务器使用密钥 1 签发,业务服务器使用密钥 2 验证——这是典型非对称加密签名的应用场景。

 

认证服务器自己使用私钥对 Token 签名,公开公钥。信任这个认证服务器的业务服务器保存公钥,用于验证签名。

 

幸好,JWT 不仅可以使用 HMAC 签名,也可以使用 RSA(一种非对称加密算法)签名。

 

不过,当业务服务器已经不受信任的时候,多个业务服务器之间使用相同的 Token 对用户来说是不安全的。

 

因为任何一个服务器拿到 Token 都可以仿冒用户去另一个服务器处理业务……悲剧随时可能发生。

 

为了防止这种情况发生,就需要在认证服务器产生 Token 的时候,把使用该 Token 的业务服务器的信息记录在 Token 中。

 

这样当另一个业务服务器拿到这个 Token 的时候,发现它并不是自己应该验证的 Token,就可以直接拒绝。

 

现在,认证服务器不信任业务服务器,业务服务器相互也不信任,但前端是信任这些服务器的——如果前端不信任,就不会拿 Token 去请求验证。

 

那么为什么会信任?可能是因为这些是同一家公司或者同一个项目中提供的若干服务构成的服务体系。

 

但是,前端信任不代表用户信任。如果 Token 不没有携带用户隐私(比如姓名),那么用户不会关心信任问题。

 

但如果 Token 含有用户隐私的时候,用户得关心信任问题了。这时候认证服务就不得不再啰嗦一些,当用户请求 Token 的时候,问上一句,你真的要授权给某某某业务服务吗?

 

而这个“某某某”,用户怎么知道它是不是真的“某某某”呢?用户当然不知道,甚至认证服务也不知道,因为公钥已经公开了,任何一个业务都可以声明自己是“某某某”。

 

为了得到用户的信任,认证服务就不得不帮助用户来甄别业务服务。所以,认证服器决定不公开公钥,而是要求业务服务先申请注册并通过审核。

 

只有通过审核的业务服务器才能得到认证服务为它创建的,仅供它使用的公钥。如果该业务服务泄漏公钥带来风险,由该业务服务自行承担。现在认证服务可以清楚的告诉用户,“某某某”服务是什么了。

 

如果用户还是不够信任,认证服务甚至可以问,某某某业务服务需要请求  A、B、C 三项个人数据,其中 A 是必须的,不然它不工作,是否允许授权?如果你授权,我就把你授权的几项数据加密放在 Token 中……

分享到:
评论

相关推荐

    Token认证签名加密

    对于初学者来说,理解并实现这个过程可能有一定难度,但通过示例代码和注解,可以更直观地了解每个步骤。在提供的压缩包文件中,可能包含了这样的示例代码,可以帮助你快速掌握Token认证签名加密的实践应用。通过...

    JFinal+token基础demo

    本教程将探讨一个基于JFinal框架的token认证基础示例,帮助开发者理解如何在实际项目中实现安全的用户登录和会话管理。JFinal是一个轻量级的Java Web框架,它以其简单、高效和灵活的特点受到了许多开发者的青睐。而...

    Token认证技术

    c#在WebAPI中使用Token认证的方式登录,增强保密。

    深入理解令牌认证机制(token)

    令牌认证机制,或者说Token,是现代Web应用中广泛采用的一种安全认证方式,特别是在前后端分离的架构中。Token主要用于在用户身份验证后提供一种安全的身份标识,使得客户端可以在不暴露用户名和密码的情况下,访问...

    springboot的模块化开发,集成shiro+jwt实现restful接口的token认证

    最近在搞springboot的模块化开发,集成了shiro+jwt实现restful接口的token认证。 需新增测试表: CREATE TABLE `t_user` ( `user_id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT, `user_name` varchar(32...

    webapi下的token认证和刷新token

    通过ajax分配相应的clientID和Secret及用户名和...测试页面click_me_please_iframe.html包含相应的刷新和认证,同时refresh_token以文件的形式进行存储,方便下次程序直接使用,不必要在产生新的token;开发工具是vs2017

    token 的来龙去脉

    在OAuth 2.0的认证流程中,首先需要获得授权码(code),然后用它来请求Access Token,通过Access Token可以获取OpenID,最后使用Access Token和OpenID来调用API获取用户授权信息。与OAuth 1.0相比,OAuth 2.0流程...

    swagger接口 token认证 redis缓存实现.rar_carsj4_redis token_swagger 测试tok

    在这个主题中,我们将深入探讨如何利用Swagger来创建对外公开的接口,并结合token认证以及Redis缓存来提升系统的安全性。 首先,让我们了解Swagger的核心功能。Swagger通过OpenAPI规范来描述API,这使得开发人员...

    SuperMap iclient for javascript使用token安全认证

    在SuperMap iClient for JavaScript中,安全认证是访问...了解以上知识点后,你就可以在SuperMap iClient for JavaScript中有效地实现基于Token的安全认证,确保你的Web应用程序能够安全地访问SuperMap iServer服务。

    spring boot+vue+websocket带token身份认证推送消息实现

    认证成功后,服务器端生成一个令牌(token),例如JWT(JSON Web Token),返回给客户端。客户端在后续的WebSocket连接请求中将此token附加在HTTP头部中,服务器端接收token并验证合法性。 ```java // 在WebSocket...

    微信token认证文件

    微信的token认证文件,只要第一次验证接口时使用就行,以后不用配置

    spring security ouath2获取token(认证)流程图.pdf

    Spring Security OAuth2 是一个强大的授权框架,用于保护Java应用程序的安全。在这个流程中,我们主要关注...这个过程是Spring Security OAuth2框架的核心部分,理解这个流程对于开发和维护安全的Web应用程序至关重要。

    SpringBoot(51) 整合Sa-Token实现权限认证

    SpringBoot(51) 整合Sa-Token实现权限认证

    java开发jwt认证 令牌,jwt.jar包 jwt 实现token认证,支持jdk1.7版本 java令牌

    jwt.jar包 jwt所需jar包集合 使用commons-codec.jar + java-jwt.jar进行token认证,支持jdk1.7及以上版本,目前大多数jwt支持至少需要1.8及以上,资源不好找,且行且珍惜。 如果需要源码以及功能实现方式,请联系...

    WebApi Token+ 数字签名认证源码

    在本文中,我们将深入探讨这个主题,重点介绍C# WebAPI中使用Token和数字签名认证的关键概念和实现步骤。 首先,让我们理解什么是Token。在Web开发中,Token通常指的是JSON Web Tokens (JWT) 或者Access Tokens。...

    jave token 认证(JWT)

    Java Token 认证,特别是JSON Web Token (JWT),在现代Web应用中被广泛用于身份验证和授权。JWT是一种轻量级的身份验证标准,允许服务端为客户端生成一个令牌,这个令牌包含了用户的相关信息,且是经过签名的,可以...

    Sa-Token权限认证框架 v1.37.0.zip

    通过学习和使用Sa-Token,学生可以深入理解权限认证的概念,掌握实际开发中的安全控制技巧,提高毕业设计的质量。 总而言之,Sa-Token是一个强大而易用的权限认证框架,它简化了权限管理的复杂性,让开发者能够更...

    web api JWT token认证

    Web API JWT Token认证是现代Web应用中一种常见的安全机制,用于保护API接口免受未经授权的访问。JWT(JSON Web Tokens)是一种轻量级的身份验证和授权机制,它允许客户端通过在请求头中发送一个令牌来证明其身份。...

Global site tag (gtag.js) - Google Analytics