看图理解JWT如何用于单点登陆

单点登陆是我比较喜欢的一个技术解决方案,一方面他可以提升产品使用的便利性,另外一方面他分离了各个应用都须要的登陆服务,对性能以及工做量都有好处。自从上次研究过JWT如何应用于会话管理,加之之前的项目中也一直在使用CAS这个比较流行的单点登陆框架,因此就一直在琢磨如何可以把JWT跟单点登陆结合起来一块儿使用,尽可能能把两种技术的优点都集成到项目中来。本文介绍我从CAS思考得出的SSO的实现方案。html

前言

其实CAS这个方案很好,很是强大,它最新的版本已经集成JWT了,因此要是不想本身开发单点登陆的服务的话,彻底能够考虑使用CAS。可是我认为,咱们在作项目的时候,也许一开始并不须要这么强大的产品,CAS提供的登陆形式有不少,而咱们只须要应用其中的一种;并且它这个框架正是由于强大,因此也会比较复杂,简单上手容易,可是遇到一些特殊的需求,好比咱们想在CAS里面加入微信登陆,那就须要对它的原理以及API有比较深刻的了解才行。综合考虑,仍是弄清楚CAS的原理,本身来实现一个基本的SSO服务比较放心。git

本文的内容须要对JWT和SSO有一个基本的了解,你能够从这两篇文章来了解JWT的用途:3种web会话管理的方式JWT实现token-based会话管理,还能够从下面的资料来了解SSO的内容:SSO_百度百科github

方案介绍

本文主要是经过时序图的方式来介绍JWT SSO的实现原理,具体的技术实现暂时尚未,不过当你理解了这个方案的原理后,你会以为最终的实现并不会特别复杂,你能够用任意的平台语言来实现它。下面的时序图,模拟了三个服务,分别是CAS、系统A、系统B,它们分别部署在cas.com,systemA.com和systemB.com;CAS这个服务用来管理SSO的会话;系统A和系统B表明着实际的业务系统。我从五个场景分别来讲明这个SSO方案的实现细节。下面先来看第一个。web

场景一:用户发起对业务系统的第一次访问,假设他第一次访问的是系统A的some/page这个页面,它最终成功访问到这个页面的过程是:redis

sso_1

在这个过程里面,我认为理解的关键点在于:算法

1. 它用到了两个cookie(jwt和sid)和三次重定向来完成会话的建立和会话的传递;缓存

1. jwt的cookie是写在systemA.com这个域下的,因此每次重定向到systemA.com的时候,jwt这个cookie只要有就会带过去;安全

2. sid的cookie是写在cas.com这个域下的,因此每次重定向到cas.com的时候,sid这个cookie只要有就会带过去;服务器

3. 在验证jwt的时候,如何知道当前用户已经建立了sso的会话?由于jwt的payload里面存储了以前建立的sso 会话的session id,因此当cas拿到jwt,就至关于拿到了session id,而后用这个session id去判断有没有的对应的session对象便可。微信

还要注意的是:CAS服务里面的session属于服务端建立的对象,因此要考虑session id惟一性以及session共享(假如CAS采用集群部署的话)的问题。session id的惟一性能够经过用户名密码加随机数而后用hash算法如md5简单处理;session共享,能够用memcached或者redis这种专门的支持集群部署的缓存服务器管理session来处理。

因为服务端session具备生命周期的特色,到期需自动销毁,因此不要本身去写session的管理,省得引起其它问题,到github里找开源的缓存管理中间件来处理便可。存储session对象的时候,只要用session id做为key,session对象自己做为value,存入缓存便可。session对象里面除了session id,还能够存放登陆以后获取的用户信息等业务数据,方便业务系统调用的时候,从session里面返回会话数据。

场景二:用户登陆以后,继续访问系统A的其它页面,如some/page2,它的处理过程是:

sso_2

从这一步能够看出,即便登陆以后,也要每次跟CAS校验jwt的有效性以及会话的有效性,其实jwt的有效性也能够放在业务系统里面处理的,可是会话的有效性就必须到CAS那边才能完成了。当CAS拿到jwt里面的session id以后,就能到session 缓存服务器里面去验证该session id对应的session对象是否存在,不存在,就说明会话已经销毁了(退出)。

场景三:用户登陆了系统A以后,再去访问其余系统如系统B的资源,好比系统B的some/page,它最终能访问到系统B的some/page的流程是:

sso_3

这个过程的关键在于第一次重定向的时候,它会把sid这个cookie带回给CAS服务器,因此CAS服务器可以判断出会话是否已经创建,若是已经创建就跳过登陆页的逻辑。

场景四:用户继续访问系统B的其它资源,如系统B的some/page2:

sso_4

这个场景的逻辑跟场景二彻底一致。

场景五:退出登陆,假如它从系统B发起退出,最终的流程是:

sso_5

最重要的是要清除sid的cookie,jwt的cookie可能业务系统都有建立,因此不可能在退出的时候还挨个去清除那些系统的cookie,只要sid一清除,那么即便那些jwt的cookie在下次访问的时候还会被传递到业务系统的服务端,因为jwt里面的sid已经无效,因此最后仍是会被重定向到CAS登陆页进行处理。

方案总结

以上方案两个关键的前提:

1. 整个会话管理其实仍是基于服务端的session来作的,只不过这个session只存在于CAS服务里面;

2. CAS之因此信任业务系统的jwt,是由于这个jwt是CAS签发的,理论上只要认证经过,就能够认为这个jwt是合法的。

jwt自己是不可伪造,不可篡改的,可是不表明非法用户冒充正经常使用法发起请求,因此常规的几个安全策略在实际项目中都应该使用:

1. 使用https

2. 使用http-only的cookie,针对sid和jwt

3. 管理好密钥

4. 防范CSRF攻击。

尤为是CSRF攻击形式,不少都是钻代码的漏洞发生的,因此一旦出现CSRF漏洞,而且被人利用,那么别人就能用得到的jwt,冒充正经常使用户访问全部业务系统,这个安全问题的后果仍是很严重的。考虑到这一点,为了在即便有漏洞的状况将损害减至最小,能够在jwt里面加入一个系统标识,添加一个验证,只有传过来的jwt内的系统标识与发起jwt验证请求的服务一致的状况下,才容许验证经过。这样的话,一个非法用户拿到某个系统的jwt,就不能用来访问其它业务系统了。

在业务系统跟CAS发起attach/validate请求的时候,也能够在CAS端作些处理,由于这个请求,在一次SSO过程当中,一个系统只应该发一次,因此只要以前已经给这个系统签发过jwt了,那么后续 同一系统的attach/validate请求均可以忽略掉。

总的来讲,这个方案的好处有:

1. 彻底分布式,跨平台,CAS以及业务系统都可采用不一样的语言来开发;

2. 业务系统如系统A和系统B,可实现服务端无状态

3. 假如是本身来实现,那么能够轻易的在CAS里面集成用户注册服务以及第三方登陆服务,如微信登陆等。

它的缺陷是:

1. 第一次登陆某个系统,须要三次重定向(不过能够优化成两次);

2. 登陆后的后续请求,每次都须要跟CAS进行会话验证,因此CAS的性能负载会比较大

3. 登录后的后续请求,每次都跟CAS交互,也会增长请求响应时间,影响用户体验。

本文小结

本文从理论层面介绍告终合jwt来实现SSO的方案原理,但愿它能帮助一些朋友更好的理解SSO以及它的实现方法。本文方案参考自CAS的实现流程,你能够从下面这个资料了解CAS的单点登陆实现过程:

https://apereo.github.io/cas/4.1.x/protocol/CAS-Protocol.html

它的流程跟我这个差异不是特别大,可是从清晰层面来讲,我写的仍是要更明了一些,因此对比起来阅读,可能理解会更透彻些。

另外,这个方案考虑地不必定很全面,因此要是您发现了其中的问题,还请您帮忙指正,很是感谢:)

相关文章
相关标签/搜索