一.前言
众所周知,IdentityServer4 默认支持两种类型的 Token,一种是 Reference Token,一种是 JWT Token 。前者的特点是 Token 的有效与否是由 Token 颁发服务集中化控制的,颁发的时候会持久化 Token,然后每次验证都需要将 Token 传递到颁发服务进行验证,是一种中心化的比较传统的验证方式。JWT Token 的特点与前者相反,每个资源服务不需要每次都要都去颁发服务进行验证 Token 的有效性验证,该 Token 由三部分组成,其中最后一部分包含了一个签名,是在颁发的时候采用非对称加密算法(最新的JWT Token)进行数据签名的,保证了 Token 的不可篡改性,保证了安全,与颁发服务的交互,仅仅是获取公钥用于验证签名,且该公钥获取以后可以自己缓存,持续使用,不用再去交互获得,除非Token包含的 keyid 对应的 公钥没被缓存(新的),就会再次向颁发服务获取。我画了一张流程图,大家可以去查看:https://www.cnblogs.com/stulzq/p/9226059.html
这里说一下我在文章说所说的名词:
颁发服务:即生成Token的服务。
资源服务:提供给用户访问的API资源
二.JWT Token 的安全问题
前言中有过叙述,JWT 类型的 Token 在验证的时候,无需依靠颁发服务来验证 Token 的有效性,是一种去中心化的验证方式,这就意味着颁发服务无法集中控制 Token。假如 Token 暴露以后,在 Token 有效期内,将会一直被人恶意使用,这时候该怎么办呢?这里主要从两个方面来讲,一个是尽量避免被恶意获取Token,一个是被恶意获取了怎么控制失效。请听下面分解。
1.使用 HTTPS
此种方式是避免被人获取恶意获取Token。
HTTPS 在传输数据时,数据内容是加密的,可以有效避免中间人攻击,所以在使用 JWT Token 的程序建议都采用HTTPS。
2.添加自定义Token失效机制
此种方式是被恶意获取了怎么控制失效。
因为 IdentityServer4 对 JWT Token,默认是没有控制失效的机制的,所以如果我们想添加这种机制,只有我们自定义,下一节做详细介绍。
三.自定义Token失效机制
1.简单黑名单模式
顾名思义,就是添加一个 Token 黑名单,这个黑名单建议存在诸如 Redis 等分布式缓存,数据库等介质,可以让所有资源服务共同访问。不推荐添加在资源服务本地缓存,如果这样做,那么每次添加黑名单还需要同步到每个资源服务。每个资源服务在每次验证Token的时候需要查询一下黑名单,如果在黑名单里面,即 Token 无效。
2.进阶黑名单模式
前面小节的 【简单黑名单模式】 有一个非常大的弊端,就是每个 Token 验证时都需要去验证是否在黑名单,正常情况下,我们正常的Token 是占绝大多数的,如果用此种机制,那么对资源是一种很大的浪费。那么我们需要设立一种机制,来让我们认为 可疑 的Token进行黑名单验证,那么如何来判断Token是否可疑呢,我这里想了一种方式。
如何判断 Token 是否可疑:
我们在生成Token的时候,可以添加自定义 Claim (身份信息单元),那么我们可以参考网站登录的安全机制,那么我们可以添加一个用户ip的Claim,这样我们生成的Token都会携带用户生成Token时的IP,我们每次验证Token是否有效时,就可以根据客户端来源IP与Token携带的IP进行匹配,如果匹配不上,那么该Token我们就可以认为是可疑的,从而进行黑名单的验证。
该方式相对于前面的 【简单黑名单模式】模式算是一个比较好的进阶了。
在这里,我们还需要考虑到IP作为用户的私密信息,我们将IP放入Token时,需要对IP进行加密。因为 JWT Token 前两部分,仅仅是 base64 Encode 而已。
Claim 详解请参考 http://www.cnblogs.com/stulzq/p/8726002.html
3.强化黑名单模式
无论是【简单黑名单模式】还是【进阶黑名单模式】,我们在对比黑名单时是对token进行完全比对,这样的方式,在某些场景就存在局限性,比我想让该用户在某某时间以前颁发的Token都算作黑名单。所以我们在判断黑名单时可以根据用户id以及token颁发时间来判断。如果让规则自动失效?我们可以用前面设定的 token颁发时间加上我们颁发服务设置的token有效时间就等于规则失效时间。
4.将Token添加进黑名单的方式
我们前面设立了黑名单模式,那么我们的Token何时加入黑名单呢,难道让用户说,我的 Token 被盗了,你把我的 Token加入黑名单吧,这肯定不现实。我们可以在退出登录时,就自动往黑名单添加一条规则,采用【强化黑名单模式】添加用户id以及当前时间作为token颁发时间来验证。比如用户id1000,此用户在 2018-09-20 12:11 退出,我们就可以添加一条规则 userid=1000,tokenissuetime=2018-09-20 12:11
,该规则表示只要用户id为1000的并且token颁发时间小于2018-09-20 12:11的token,都被算作黑名单token。
这时有人可能会说,这个token如果还是这个用户再次拿来使用,那还是有效的,你这个怎么没让他失效呢?我们设立黑名单模式就是为了避免用户的还在有效期的Token被他人恶意使用。对于用户自己来说,这个问题就无关紧要了。
5.全部 Token 失效的机制。
全部Token失效的方式,目前我想了两种:
1.更换颁发服务的密钥对,并且重启所有资源服务(资源服务获取的公钥默认存在内存,重启可以丢失)。这样原本的Token在验证时,将会找不到对应的公钥,导致验签失败从而Token无效。
2.类似于前面【强化黑名单模式】的验证黑名单的方式,我们可以在验证Token的流程中加两个配置,一个是控制这种配置是否开启的开关,一个是某个时间,规则就是如果在这个时间以前颁发Token全部算作无效Token。这种就需要资源服务支持热加载配置,从而避免重启资源服务。
我个人推荐第二种方式。
四.写在最后
文中所诉是总结了我长久以来的想法,token加入ip还有根据id和颁发时间验证黑名单都是我今天无意间想到的。如果你阅读了本文有什么不明白或者你认为有改进的地方,或者更好的地方,欢迎在评论与我交流。本文的问题,有很多人问过我,也讨论了不少,我相信很多人在使用ids4是可能会有这样的问题,所以在此发表了我的一个观点,希望能给你参考,后续的文章我会根据这个想法来实现。
原文地址:https://www.cnblogs.com/stulzq/p/9678501.html