单点登录(Single Sign On)实现原理详解
Posted 零君聊软件
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单点登录(Single Sign On)实现原理详解相关的知识,希望对你有一定的参考价值。
单点登录(SSO)一般人都不陌生。用户只需记住一个用户名/密码,就可以访问所有支持SSO的web应用(一般是同一个公司提供的多个应用)。而且用户只需在打开浏览器后第一次访问的时候需要登录一次,以后再访问其它应用时就无需再次登录。所以SSO极大的提高了用户体验。
与SSO对应的就是Single Sign Off,或者称为Single Logout(SLO)。也即是在退出任何一个应用时,应该同时退出所有当前浏览器登录过的应用。本文会详细描述SSO和SLO的原理。
网上有一些介绍SSO原理的文章,但基本都比较抽象或过于简单,对于没有相关的经验的读者,很容易迷惑。本文不仅描述了基本原理,也还原了真实详细的HTTP交互过程,所以阅读本文需要一定的耐心。
首先明确一下本文涉及到的几个概念:
1、browser
也就是用户浏览器,是发起访问的客户端。这里一定要明确一点,单点登录(SSO)和单点登出(SLO)都是针对同一个浏览器客户端而言的。也就是说SSO和SLO只对同一个浏览器内的多个TAB页之间有效,而对于不同浏览器之间无效。例如,你打开chrome之后,访问webapp1时进行身份验证登录之后,然后在同一个浏览器内开一个新的TAB对webapp2进行访问时,是无需再次进行身份验证;但是如果你再打开一个Firefox访问webapp1或webapp2,还是需要登录的。原因很简单,不同的浏览器之间cookie是相互独立的。
2、webapp
为了便于描述,本文用webapp1和webapp2来表示两个不同的web application。他们都支持SSO和SLO。
3、Identity Provider(IdP)
Identify Provider(简称IdP)主要作用是提供身份识别服务。一个公司内一般有多个web applications,可以让所有web apps都通过一个IdP来进行身份验证。本文主要以Oracle IDCS(Identity Cloud Service)为例来说明,但SSO/SLO的原理都是相通的。
注册webapp
首先要在IdP那里注册所有需要支持SSO/SLO的web application。例如,某公司有5个web应用程序:人力资源系统、IT支持系统、Bug管理的JIRA系统、wiki系统、出差管理系统。要对这5个系统实现SSO/SLO,那么首先要将这5个系统在IdP那里注册,注册时要提供一些信息,其中关键几个如下:
(1) Redirect URL
用户通过身份验证后要转向的URL。
(2) Logout URL
用户在登出(Logout)过程中,IdP会调用的URL。
(3) Post Logout Redirect URL
用户完成登出(Logout)之后,会转向的URL。
注意,上面三个URL都是各个webapp的URL,下文会详细描述他们的作用。
注册之后,IdP会为每一个web application生成一个Client ID和Client Secret。
这个过程也比较容易理解,SSO/SLO需要browser(用户)、web application、IdP三方的配合才能实现。IdP那里有所有用户的身份信息,同样IdP那里也需要所有web application的信息。只有在IdP那里注册的用户和web application才能参与到SSO/SLO的过程中。
单点登录(Single Sign On)原理
首先用1~2句话高度概括SSO的原理。当用户访问一个webapp1时,webapp1发现该用户没有登录,就会转到IdP进行身份验证;这时用户需要输入用户名/密码,IdP完成身份验证之后,用户就可以正常访问webapp1了。当用户继续访问另一个webapp2时,webapp2发现该用户没有登录,同样会转到IdP进行身份验证;但由于用户之前已经在IdP进行过身份验证,所以这次用户无需再次进行身份验证,IdP直接将用户browser转向webapp2。
下图是SSO完整的HTTP的交互过程(可放大查看):
SSO详细的HTTP交互过程如下:
1、首先browser请求访问webapp1;
2、webapp1会验证当前的session/cookie,来判断当前用户是否已经完成了身份验证。注意,这时验证的是browser和webapp1之间的session/cookie,与IdP还没有任何关系。如果用户已经验证过,也就是browser和webapp1之间的session有效,那么browser可以正常访问webapp1,后面的步骤3~17就无需执行了。如果用户没有验证过,则继续后面的步骤。
3、webapp1将用户转发到IdP进行身份验证。所以这里需要从IdP那里获取详细的身份验证的URL。
注:这里webapp1是通过REST API直接和IdP交互。
4、IdP将身份验证的具体URL返回给webapp1;当然还有其它一些信息,不过我们目前对其它信息不用关心。
5、webapp1收到IdP送回的身份验证URL之后,在URL后面增加适当的参数,然后将用户browser直接转向这个URL。
用node.js来实现的话,其实就是下面的语句:
reply.redirect(authZurl);
6、用户browser会收到一个状态码为302的HTTP响应,HTTP响应头中有一个field:"location",它的值就是IdP身份验证的URL,就是指示browser重定向到这个URL。
7、IdP收到来自用户browser的身份验证请求之后,自然会检查该用户是否已经验证过,其实也就是检查browser和IdP之间的session/cookie。
8、由于用户browser之前没有登录过,所以将用户browser重定向到了IdP的登录界面。
注:这时IdP给用户browser返回的HTTP状态码是303,HTTP响应头中的field: "location"是IdP登录界面的URL。
到目前为止,所有的交互过程都是步骤1(用户browser请求访问webapp1)触发后自动发生的。
9、这时用户browser上看到的应该是IdP的登录界面。用户输入并提交自己的username/password。
10、IdP验证用户的username/password之后,会建立browser与IdP之间的session、设置好cookie,并且为webapp1生成一个authorization code。注意这里设置的cookie是browser与IdP之间的,与任何web application没有关系。
之前在IdP注册每个webapp时,配置了一个“Redirect URL",可以称为postlogin callback。IdP完成对用户的身份验证之后,就会将用户browser转向webapp1的postlogin callback,分配的authorization code会作为URL的参数一起传回browser。
11、 用户browser请求webapp1的postlogin callback。
12、webapp1处理postlogin callback请求。步骤12~15都是处理的过程。首先是用收到的authorization code通过REST API向IdP请求tokens(access_token, id_token等)。
注:之前注册webapp1时,IdP为webapp1分配的client ID和Client Secret需要作为REST API的参数传到IdP。
13、IdP返回tokens到webap1。
14、webapp1建立用户browser与webapp1之间的session、设置好cookie。注意这里的cookie是browser与webapp1之间的,与其他webapps以及IdP没有关系。
注:上面的身份验证的过程采用的是OAuth2.0授权码模式。
15~17、至此,完成了整个身份认证的过程。最终webapp1的homepage将呈现在用户browser里面。
18~23、当用户在同一个浏览器中继续访问webapp2时,大致上是重复上面的过程。webapp2会验证browser与webapp2之间的session/cookie,发现用户没有验证(注:这里是指用户browser没有经过webapp2的身份验证),就会将用户browser转向IdP的身份验证的URL。
24~32、IdP接收到用户browser的身份验证请求之后,检查browser与IdP之间的session/cookie,由于用户之前已经通过了IdP的身份验证,所以这次IdP并不是转向IdP的登录界面, 而是直接将用户browser转向webapp2的postlogin callback,authorization code作为参数一并返回给browser。随后的过程与webapp1就完全一样了。
单点登出(Single Sign Off,Single Logout)原理
单点登出是指当用户登出某个webapp时,那么所有当前浏览器登录过的webapps都应该登出。用一句话概括SLO的原理就是:当用户登出某个webapp时,IdP会通知当前browser登录过的所有webapp登出。
假设用户browser已经登录了webapp1和webapp2,那么当用户登出webapp1时,详细地流程如下图所示(可放大查看):
SLO详细的HTTP交互过程如下:
1、首先browser请求登出webapp1。
2~3、webapp1直接将用户browser转向IdP的登出(Logout)URL。
注:你可能会问:为什么不先清除browser与webapp1之间的session/cookie呢?继续看下去就自然明白了。
4~9、IdP在接收到Logout URL后,会将当前browser登录过的所有webapp的Logout URL返回给用户browser。
注:关于“Logout URL"的定义,请查看本文前面的“注册webapp”小节。
IdP返回给用户browser的代码如下:
<!DOCTYPE html>
<html>
<head>
<META HTTP-EQUIV='refresh' CONTENT='1;URL=https://www.exampleIdP.com/sso/v1/user/logout'>
<br>
<img align='center' style='display: none;' alt='...' border='1' width='40' height='20' src='http://www.webapp1.com/callback/logout'/>
<br>
<img align='center' style='display: none;' alt='...' border='1' width='40' height='20' src='http://www.webapp1.com/callback/logout'/>
</head>
</html>
这一步是SLO的关键所在,虽然不同的IdP实现细节可能不同,但都大同小异。
这段代码巧妙的将各个webapp的logout URL作为img的src属性返回给客户端,从而达到了通知所有相关webapp登出的目的。可能有人会问,为什么IdP不能直接调用各个webapp的logout URL来进行通知呢?因为通知各个webapp登出主要是要清除browser与各个webapp之间的session/cookie信息,而IdP是不可能知道这些信息的。网上绝大部分文章在介绍SLO时配的图基本都是IdP直接回调各个webapp的logout callback,是有一定的迷惑性的。
各个webapp在logout callback中清除browser与该webapp之间的session/cookie。注意,一定要在logout callback中清除session/cookie,而不能在第2步中清除。只有在logout callback中清除session/cookie才能实现SLO,因为logout callback可能会被任何一个webapp的登出请求触发。
下面的代码意思是1秒之后转向IdP的logout URL(注意这个URL与之前的IdP的那个logout URL不同),
<META HTTP-EQUIV='refresh' CONTENT='1;URL=https://www.exampleIdP.com/sso/v1/user/logout'>
10、IdP清除用户browser与IdP之间的session/cookie。最后将用户browser转向webapp1之前注册时的Post Logout Redirect URL。
11~21、用户浏览器请求webapp1的Post Logout Redirect URL。webapp1会验证browser与webapp1之间的session,由于session已经失效,所以会重复登录时的过程,最终重定向到IdP的登录界面。有人可能会问,步骤11~21有什么意义?意义就在于用户再次在IdP的登录界面输入并提交username/password之后,还会重新回到webapp1的homepage。当然,你也可以在Post Logout Redirect 页面直接显示一行字“You have already logged out!”。
注:SLO的整个过程都是在用户点击退出webapp1之后自动完成的。
随后,当用户继续在webapp2的界面操作时,由于browser和webapp2之间的session/cookie已经失效,所以会重定向到IdP的登录界面。
SSO与SLO具体实现
SSO与SLO的实现包含两个方面。一是IdP的实现;二是各个webapp如何与IdP集成。IdP的实现不是本文讨论的重点。IdP一般会提供多种语言的SDK,以方便各个webapp与IdP集成。当然webapp也可以不使用IdP提供的SDK, 直接通过REST API与IdP交互。
各个webapp虽然通过IdP进行身份验证,但它们自己管理browser与webapp之间的session/cookie。各个webapp实现SSO与SLO的关键在于实现postlogin callback与logout callback。一般在postlogin callback中建立browser与webapp之间的session,而在logout callback中销毁browser与webapp之间的session。
虽然postlogin callback和logout callback在概念来说,是IdP回调webapp,但实际上是通过browser的重定向实现的。
--END--
以上是关于单点登录(Single Sign On)实现原理详解的主要内容,如果未能解决你的问题,请参考以下文章
SSO(single sign on)模式 --单点登录三种登录方式
SSO(single sign on)模式 --单点登录三种登录方式
SSO(single sign on)模式 --单点登录三种登录方式