之前在学校作项目的时候,登陆注销,权限验证这些事情,都是交给框架来作的,每次都是把这个架子拿到项目中去,也没有真正思考过它的过程,总觉的这些都是十分简单的逻辑。前端
然而来公司工做以后,慢慢以为登陆和权限虽然固定,但确实不容出错的。而且,在一个大型的公司或是多系统中,登陆和权限都是抽离开来的,它们有复杂的逻辑以及高安全性,而且使得多个系统能够有一个统一登陆和认证的接口。这就是今天想描述的单点登陆SSO。nginx
1. 关于SSO:ajax
SSO是一种统一认证和受权机制,指访问同一服务器不一样应用中的受保护资源的同一用户,只须要登陆一次,即经过一个应用中的安全验证后,再访问其余应用中的受保护资源时,再也不须要从新登陆验证。后端
它包含的核心有两点:浏览器
1)全部应用系统共享一个身份认证系统。安全
统一的认证系统是SSO的前提之一。认证系统的主要功能是将用户的登陆信息和用户信息库相比较,对用户进行登陆认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行效验,判断其有效性。服务器
2)全部应用系统可以识别和提取ticket信息cookie
要实现SSO的功能,让用户只登陆一次,就必须让应用系统可以识别已经登陆过的用户。应用系统应该能对ticket进行识别和提取,经过与认证系统的通信,能自动判断当前用户是否登陆过,从而完成单点登陆的功能。session
那具体公司的SSO是怎样的原理和过程呢?架构
首先是我看到的一个原理图:
其实主要包含两种状况:
1. 当在统一认证处没有认证,或者自己访问子系统浏览器客户端没有cookie保存的ticket时,会进行帐号密码登陆的过程,登陆成功便会经过回调的url返回code给子系统。这时子系统须要拿code到checkCodeUrl去拿ticket和用户信息。拿到userInfo和ticket后,能够作一些权限的调用,也能够对ticket,userInfo信息进行处理加密生成token串(这里咱们用的是JWT,一会会讲到),而后讲token放入客户端的cookie中,以便下次使用。
2. 当访问子系统的浏览器有token信息时,则会经过JWT进行token串的解密,拿到userInfo和ticket,而后携带ticket去checkTicketUrl去校验ticket,而若是统一认证处保存了用户的登陆信息,则认证经过。
讲完这两种状况,想贴一下我前两天看了这部分代码的画的一个逻辑草图:
这里还有一点在wiki上看到了,想记录一下:
因为全部项目都采用先后端分离设计,而先后端分离后,静态页面没法受到后端服务的权限控制。一般做法是页面请求后发ajax到后端验证,若是验证失败后后端返回跳SSO的必要数据,由前端再302到SSO登陆页。但这并不符合SSO的接入标准的要求。
如今的解决方式是:
后端加入对根路径的请求拦截。以如今前端架构设计看,相似于http://domain/#/XXX的请求格式,其实都是对根路径的请求。
在nginx中加入转发规则,对根路径的请求转到后端服务器上。
后端接收到页面请求后先经过SSO Check,若是验证不经过,在后端直接发起重定向到SSO登陆页。
2. 关于JWT
首先想比较一下传统的session认证和token的认证方式:
http协议自己是一种无状态的协议,而这就意味着若是用户向咱们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,由于根据http协议,咱们并不能知道是哪一个用户发出的请求,因此为了让咱们的应用能识别是哪一个用户发出的请求,咱们只能在服务器存储一份用户登陆的信息,这份登陆信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给咱们的应用,这样咱们的应用就能识别请求来自哪一个用户了,这就是传统的基于session认证。基于session的认证使应用自己很可贵到扩展,随着不一样客户端用户的增长,独立的服务器已没法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来。
总结一下,session的认证方式,除了增长服务器压力,还会致使很差扩展,分布式环境会涉及到session同步的问题,而且还会容易受到CSRF攻击(这个我尚未细看)。
那么token的方式就显得十分轻便和灵活了。客户端负责存储token(通常用cookie做为存储方式),并在每次请求时附送上这个token值,由服务端完成校验(就是刚刚SSO的那个流程),这样一来,也为扩展提供了遍历,客户端不须要知道请求哪一台服务器。
而后关于JWT的格式,这里就不细说了,它有三段部分,并用到了Base64加密,使得信息更加安全。经过jwt工具的加密和解析能够很方便的把须要的信息存入token,知足系统须要的各个需求。
最后再说两个点吧:
1) 因为jwt是对称加密,是能够被解密的,因此有些极为敏感的数据仍是不要放在token中为好。
2) jwt还支持token的过时处理和刷新等功能,但咱们的系统中并无用到,若是从此有这方面的需求,能够作一些改进吧。