单点登录的实现方式
该篇文章很多段落来自单点登录原理与简单实现,大家可以先拜读这篇文章,这篇文章讲述十分详细,我这里只是插了一些自己的疑虑,而且可能还说的不是很清楚,权当自己做个笔记了
单系统登录机制
- http无状态协议
http作为通信协议,是无状态的,浏览器的每一次请求,服务器会独立处理,不与之前或之后的请求产生关联。
但这也同时意味着,任何用户都能通过浏览器访问服务器资源,如果想保护服务器的某些资源,必须限制浏览器请求;要限制浏览器请求,必须鉴别浏览器请求,响应合法请求,忽略非法请求;要鉴别浏览器请求,必须清楚浏览器请求状态。既然http协议无状态,那就让服务器和浏览器共同维护一个状态吧!这就是会话机制。
- 会话机制
浏览器第一次请求服务器,服务器创建一个会话,并将会话的id作为响应的一部分发送给浏览器,浏览器存储会话id,并在后续第二次和第三次请求中带上会话id,服务器取得请求中的会话id就知道是不是同一个用户了。
这里有个session和cookie的区别;其实session算是cookie的变种;因为本身的机制形式并没有变,依然是需要浏览器有一个信息串和服务器得会话对象所对应,只不过早先得cookie是在浏览器端保存得用户的信息,服务器通过这些信息来识别用户,而session方式是把信息保存在了服务器端,然后生成一个session的id字符串存在了cookie,相对之前的做法安全一些;但本质未发生变化。
当然,浏览器但端的信息其实不是必须存在cookie里,也可以放在localstotage或者其他可存的地方,这样防止从cookie得到敏感信息,但这样就需要前端做一个手工处理,将这个id发送给服务器端;目前大多的系统还是主要以cookie来存储的,一是读写cookie浏览器可以自动完成,二是在单点登录系统时可以帮其他子应用自动登录。
- 登录状态
有了会话机制,登录状态就好明白了,我们假设浏览器第一次请求服务器需要输入用户名与密码验证身份,服务器拿到用户名密码去数据库比对,正确的话说明当前持有这个会话的用户是合法用户,应该将这个会话标记为“已授权”或者“已登录”等等之类的状态。
个人理解,http是无状态的,那么每个受限请求都是需要验证的,那么前端传来的id只要可以对应后端的session,那么就是合法的,所以不太清楚后端是否还要做一个会话标记的操作;
多系统的复杂性
web系统由单系统发展成多系统组成的应用群,复杂性应该由系统内部承担,而不是用户。无论web系统内部多么复杂,对用户而言,都是一个统一的整体,也就是说,用户访问web系统的整个应用群与访问单个系统一样,登录/注销只要一次就够了。
单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器与服务器之间维护会话状态。但cookie是有限制的,这个限制就是cookie的域(通常对应网站的域名),浏览器发送http请求时会自动携带与该域匹配的cookie,而不是所有cookie。也就是说A应用登录后,打开B应用,B是拿不到A的cookie的。
这时我们就将web应用群中所有子系统的域名统一在一个顶级域名下,在顶级域名下设置的cookie,其子域名是可以访问其cookie的。
这里有个cookie设置的规则(有点儿像全局作用域和子作用域):
设置cookie只可以向上设置,可设置本级域名,或者向上的域名;比如顶级域名只能设置domain为顶级域名,不能设置为二级域名或者三级域名等等,否则cookie无法生成;二级域名可以设置本域及顶级域;
获取cookie只可以向上获取,可获取本级域名,或者向上的域名;比如顶级域名只能获取到domain设置为顶级域名的cookie,domain设置为其他子级域名的无法获取;二级域名能读取设置了domain为顶级域名或者自身的cookie,不能读取其他二级域名domain的cookie。
总的来说,顶级域名设置的cookie可以共享【需要指定domain主域名的host】给二级域名,也可以自己私有【不指定domain】
- 这里云平台系统采用的就是这种方式,sessionid由统一认证系统生成,并写到顶级域名下面,其余子系统通过访问cookie获取该id与统一认证系统进行权限验证;不过这里是子系统通过统一认证系统生成的token然后在服务端去统一认证系统换取sessionid,由子系统写到cookie里的顶级域名下的,不太清楚这一步是什么意思;这样,各个子系统的共享统一生成的sessionid,并且每个受限请求都去找认证系统授权;这种方式也很方便的可以做到统一登出,只要使统一认证系统的session失效就可以了。
然而,可行并不代表好,共享cookie的方式存在众多局限。首先,应用群域名得统一;其次,应用群各系统使用的技术(至少是web服务器)要相同,不然cookie的key值(tomcat为JSESSIONID)不同,无法维持会话,共享cookie的方式是无法实现跨语言技术平台登录的,比如java、php、.net系统之间;第三,cookie本身不安全。
因此,我们需要一种全新的登录方式来实现多系统应用群的登录,这就是单点登录。
单点登录
这里说的单点登录,是指有一个统一认证中心,可实现一处登录,全都登录,一处登出,全部登出,并且不局限与同一个顶级域名下。
单点登录的简单流程如下图:


其中有两个主要注意的地方,就是在认证中心登录的时候,会帮其他子系统都登录,登出的时候会帮其他子系统都登出;另外子系统的sessionid是各自维护还是统一有认证系统维护;
- 统一登录登出机制
在统一认证中心登录后,可以以跨域访问的形式,将统一的生成的token,逐个发送给各个子系统,子系统收到后向认证中心认证token,认证通过后可以给自己系统下写入cookie,这样,在访问其他子系统的时候,请求会带上之前写的cookie,从而打开子系统即为登录状态;
在子系统登出时会跳转到登录页面,此时可以以跨域访问的形式,逐个访问各个子系统请求,这些请求会携带各自域名下的sessionid,子系统就会注销该id,完成各个子系统的登出;
- sessinid维护方式
如果各自维护sessionid,登录的时候不通知子系统的话,打开其他子系统的时候会有一个重定向到认证中心获取状态的过程;通知了的话就无需重定向了;在登出的时候认证中心必须去通知各个子系统登出,不然子系统无法知道已登出;
如果认证系统统一维护sessionid的话,登录不通知也会去重定向;登出就不必通知了。
- 如金融的sso认证系统便没有去通知其他子系统登录状态,在访问子系统的时候会重定向到认证地址,认证地址根据cookie信息,确认已登录后,将token带在请求上,重定向回子系统,子系统拿到token后去认证中心通过后再写sessionid,这样会有一个重定向url变更的过程;而登出的时候,由于并未通知其他子系统,所以登出无法做到统一登出;不过,此处既然没有做统一登出,那么在登录的时候也不应该去通知子系统去登录;这样这个系统其实算是个统一认证中心,不算严格意义的单点登录。
- 通知其他子系统登录登出的形式
由于域名不同,通知其他子系统只能以跨域的形式去访问各自的服务器;可以以jsonp的形式,也可以是cors的形式,或者其他跨域方式,但需要将统一认证生成的token信息手动加到请求中,因为跨域请求不会携带本域的cookie信息。