小猪站长带你深入了解Token 认证
发表于 2019-08-03 20:13
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在缓存或内存中过期。
还有另一个选项,使用Refresh Token,它可以避免频繁的读写操作。在此解决方案中,服务器不需要刷新令牌的过期时间。一旦令牌过期,它将被反馈回前端。前端使用Refresh Token来申请要继续使用的新Token。在该方案中,当客户端请求更新Token时,服务器只需要检查刷新Token的有效性,大大减少了更新有效期的操作,避免了频繁的读写。当然,Refresh Token也有一个有效期,但是这个有效期可以更长,例如,以天为单位的时间。
时序图表示
使用 Token 和 Refresh Token 的时序图如下:
1)登录
2)业务请求
3)Token 过期,刷新 Token
上面的时序图没有提到Refresh Token 是如何过期的。但是,很明显,由于Refresh Token 已经过期,应该要求用户重新登录。
当然,这个机制可以设计得更复杂。例如,刷新Token更新过期时间每次使用它,直到它已经被拿来和成立时间很长一段时间(比如三个月),这相当于允许刷新Token自动更新相当一段时间。
到目前为止,Token是有状态的,即服务器需要保存和记录相关属性。说的好无状态,怎么实现呢?
无状态 Token
如果我们将所有状态信息附加到Token,服务器可以保存它。但是,服务器仍然需要对Token进行身份验证。但是,只要服务器能够确认它是由自己发出的Token,并且它的信息没有被更改,那么就可以认为该Token是有效的——可以保证“签名”。通常,签名由一方签名,另一方验证,因此使用非对称加密算法。但是在这里,发布和验证是相同的,所以对称加密算法可以满足要求,而且对称算法比非对称算法快得多(相差几十倍)。
如果我们将所有状态信息附加到Token,服务器可以保存它。但是,服务器仍然需要对Token进行身份验证。但是,只要服务器能够确认它是由自己发出的Token,并且它的信息没有被更改,那么就可以认为该Token是有效的——可以保证“签名”。通常,签名由一方签名,另一方验证,因此使用非对称加密算法。但是在这里,发布和验证是相同的,所以对称加密算法可以满足要求,而且对称算法比非对称算法快得多(相差几十倍)。
然而,在使用无状态Token时,服务器中有一些更改。虽然服务器不保存有效的Token,但它需要保存尚未过期但已注销的Token。如果用户主动过期了Token,则服务器需要保存已注销的Token,以便在下一次仍在使用Token使其失效。你觉得有点沮丧吗?
在前端可控的情况下(例如,前端和服务器在同一个项目组中),可以协商:一旦前端成功注销,本地保存的Token和刷新Token(如内存、本地存储等)将被丢弃。根据这种约定,服务器可以假定接收到的令牌必须不注销(因为注销后前端将不再使用)。
如果前端是不可控制的,仍然可以做上述假设,但在这种情况下,有必要缩短Token的有效期,如果用户主动注销,则刷新Token必须无效。这个操作中存在一个安全漏洞,因为用户会认为它已经注销了,实际上已经有很短一段时间没有注销了。如果在应用程序设计中没有丢失漏洞,则该策略是可行的。
在使用无状态 Token 的时候,有两点需要注意:
Refresh令牌在很长一段时间内都是有效的,因此在服务器端它应该是有状态的,以增强安全性,并确保用户可以控制何时退出。
应该考虑二级身份验证来增强敏感操作的安全性
此时,关于Token的主题似乎几乎是相同的——但并非如此,上面只是集成了身份验证服务和业务服务的情况。如果是单独的情况呢?
分离认证服务
当Token处于无状态时,单点登录变得更容易。前端获得一个有效的令牌,它可以在同一系统的任何服务上进行身份验证——只要它们使用相同的密钥和算法来验证令牌的有效性。就像这样:
当然,如果 Token 过期了,前端仍然需要去认证服务更新 Token:
可以看出,虽然认证和业务是分开的,但是实际情况并没有太大的区别。当然,这是以身份验证服务器信任服务服务器为前提的,因为身份验证服务器生成Token的密钥与服务服务器身份验证Token的密钥和算法相同。换句话说,业务服务器也可以创建一个有效的令牌。
如果不能信任业务服务器怎么办?
不受信的业务服务器
当遇到不受信任的业务服务器时,很容易想到使用不同的密钥。身份验证服务器使用密钥1进行签名,服务服务器使用密钥2进行身份验证——这是典型的非对称密码签名的应用程序场景。身份验证服务器本身使用私钥签署Token并公开公钥。信任身份验证服务器的业务服务器存储公钥,并用于验证签名。幸运的是,JWT不仅可以使用HMAC签名,还可以使用RSA(一种非对称加密算法)签名。
然而,当业务服务器已经不受信任时,在多个业务服务器之间使用相同的Token对用户来说是不安全的。因为任何获得Token的服务器都可以将用户伪装到另一台服务器来处理业务……悲剧随时可能发生。
为了防止这种情况的发生,有必要记录的信息服务的服务器使用Token在Token认证服务器生成Token,这样,当另一个服务服务器获得Token,它发现它不是验证Token可以直接拒绝。
现在,身份验证服务器不信任业务服务器,业务服务器彼此不信任,但是前端信任这些服务器——如果前端不信任,它将不接受Token来请求身份验证。为什么信任呢?这可能是因为这些是同一公司的服务系统或同一项目中提供的多个服务。
然而,前端信任并不代表用户信任。如果Token不包含用户隐私(例如名称),用户就不会关心信任问题。但是,如果Token包含用户隐私,则用户必须关心信任问题。此时,身份验证服务必须多一点。当用户请求Token时,询问最后一句话,您真的要授权某个业务服务吗?而这个“东西”,用户如何知道它是否是真正的“东西”呢?当然,用户不知道,甚至认证服务也不知道,因为公钥已经公开,任何企业都可以声明它是“某物”。
为了获得用户的信任,认证服务必须帮助用户识别业务服务。因此,身份验证服务器决定不公开公钥,而是要求业务服务先申请注册并通过审核。只有通过审核的业务服务器才能获得身份验证服务为其创建的公钥(仅供其使用)。如果业务服务泄漏了公钥,则由业务服务负责。现在,身份验证服务可以清楚地告诉用户“something”服务是什么。如果用户仍然不够信任,认证服务甚至可以询问某个业务服务是否需要请求a、B、C三个个人数据,其中a是必须的,否则无法工作,是否允许授权?如果您授权,我将在Token中加密您授权的几个数据…
这么多废话,有没有很熟悉……是的,这类似于开放API认证过程。开发API主要使用OAuth认证,OAuth的资源非常丰富。我就不详细讲了。
评论 (0人参与)
最新评论