基于OIDC(OpenID Connect)的SSO
在[认证授权]系列博客中,分别对OAuth2和OIDC在理论概念方面进行了解释说明,其间虽然我有写过一个完整的示例(https://github.com/linianhui/oidc.example),但是却没有在实践方面做出过解释。在这里新开一个系列博客,来解释其各种不同的应用场景。因为OIDC是在OAuth2之上的协议,所以这其中也会包含OAuth2的一些内容。
OIDC协议本身有很多的开源实现,这里选取的是基于.Net的开源实现基于IdentityServer4。本系列的源代码位于https://github.com/linianhui/oidc.example。clone下来后用管理员身份运行build.ps1来部署整个系统,其中可能会弹出UAC警告(脚本会修改host文件,记得允许管理员读写这个文件先)。部署完后的样子如下:
本文中主要是关注一下SSO这部分的内容,主要是跨一级域的单点登录和统一登出功能。其中涉及到的站点有一下4个:
oidc-server.dev:利用oidc实现的统一认证和授权中心,SSO站点。
oidc-client-hybrid.dev:oidc的一个客户端,采用hybrid模式。
oidc-client-implicit.dev:odic的另一个客户端,采用implicit模式。
oidc-client-js.dev:oidc的又一个客户端,采用implicit模式,纯静态网站,只有js和html,无服务端代码。
单点登录
通常来讲,SSO包括统一的登录和统一的登出这两部分。基于OIDC实现的SSO主要是利用OIDC服务作为用户认证中心作为统一入口,使得所有的需要登录的地方都交给OIDC服务来做。更直白点说就是把需要进行用户认证的客户端中的用户认证这部分都剥离出来交给OIDC认证中心来做。具体的交互流程如下:
其中这三个客户端是完全独立的位于不同的域名之下,且没有任何依赖关系,三者均依赖oidc-server.dev这个站点进行认证和授权,通信协议为HTTP,那么下面则通过它们之间的HTTP消息来解释其具体的流程。这个过程中使用fiddler来进行监视其所有的请求。
第1步:OIDC-Client- 触发认证请求
在浏览器打开oidc-client-implicit.dev这个站点,打开后如下(QQ这个先不管它,后面单独介绍)。
点击Oidc Login后,会触发一个302的重定向操作。具体的HTTP请求和响应信息如下:
Request:Get后面的URL是我们点击Oidc Login的Url,这个URL包含一个参数,代表登录成功后所要回到的页面是哪里。
Response:服务器返回了一个302重定向。
Location的Url指向了oidc-server.dev这个站点,其中还携带了一大堆参数(参数后面一小节介绍);
Set-Cookie设置了一个nonce的cookie,主要目的用于安全方面。
第2步:OIDC-Client - 认证请求
紧接上一步,浏览器在接收到第1步的302响应后,会对Location所指定的URL发起一个Get请求。这个请求携带的参数如下:
其中参数的含义在OIDC的认证请求有详细的解释(注:其中采用的认证类型不管是authorization code,或者implict,还是hybrid都无关紧要,它们的区别只是其适用场景的差异,并不影响整个流程)。
client_id=implicit-client:发起认证请求的客户端的唯一标识,这个客户端事先已经在oidc-server.dev这个站点注册过了。
reponse_mode=form_post:指示oidc服务器应该使用form表单的形式返回数据给客户端。
response_type=id_token:区别于oauth2授权请求的一点,必须包含有id_token这一项。
scope=openid profile:区别于oauth2授权请求的一点,必须包含有openid这一项。
state:oauth2定义的一个状态字符串,这里的实现是加密保存了一些客户端的状态信息(用于记录客户端的一些状态,在登录成功后会有用处),oidc会在认证完成后原样返回这个参数。
nonce:上一步中写入cookie的值,这字符串将来会包含在idtoken中原样返回给客户端。
redirect_uri:认证成功后的回调地址,oidc-server.dev会把认证的信息发送给这个地址。
第3步:OIDC-Server - 验证请求信息
oidc-server.dev站点会验证第2步中传递过来的信息,比如client_id是否有效,redircet_uri是否合法,其他的参数是否合法之类的验证。如果验证通过,则会进行下一步操作。
第4步:OIDC-Server - 打开登录页面
在oidc-server.dev站点验证完成后,如果没有从来没有客户端通过oidc-server.dev登陆过,那么第2步的请求会返回一个302重定向重定向到登录页面。如果是已经登录,则会直接返回第5步中生产重定向地址。
浏览器会打开响应消息中Location指定的地址(登录页面)。如下:
第5步:OIDC-Server - 完成用户登录,同时记录登录状态
在第四步输入账户密码点击提交后,会POST如下信息到服务器端。
服务器验证用户的账号密码,通过后会使用Set-cookie维持自身的登录状态。然后使用302重定向到下一个页面。
第6步:浏览器 - 打开上一步重定向的地址,同时自动发起一个post请求
form的地址是在第2步中设置的回调地址,form表单中包含(根据具体的认证方式authorization code,implict或者hybrid,其包含的信息会有一些差异,这个例子中是采用的implicit方式)如下信息:
id_token:id_token即为认证的信息,OIDC的核心部分,采用JWT格式包装的一个字符串。
scope:用户允许访问的scope信息。
state:第1步中发送的state,原样返回。
session_state:会话状态。
id_token包含的具体的信息如下:
其中包含认证的服务器信息iss,客户端的信息aud,时效信息nbf和exp,用户信息sub和nickname,会话信息sid,以及第1步中设置的nonce。还有其签名的信息alg=RS256,表示idtoken最后的一段信息(上图中浅蓝色的部分)是oidc-server.dev使用RSA256对id_token的header和payload部分所生产的数字签名。客户端需要使用oidc-server.dev提供的公钥来验证这个数字签名。
第7步:OIDC-Client - 接收第6步POST过来的参数,构建自身的登录状态
客户端验证id_token的有效性,其中验证所需的公钥来自OIDC的发现服务中的jwk_uri,这个验证是必须的,目的时为了保证客户端得到的id_token是oidc-sercer.dev颁发的,并且没有被篡改过,以及id_token的有效时间验证。数字签名的JWT可以保证id_token的不可否认性,认证和完整性,但是并不能保证其机密性,所以id_token中千万不要包含有机密性要求的敏感的数据。如果确实需要包含,则需要对其进行加密处理(比如JWE规范)。其中验证也包含对nonce(包含在id_token中)的验证(第1步设置的名为nonce的cookie)。
在验证完成后,客户端就可以取出来其中包含的用户信息来构建自身的登录状态,比如上如中Set-Cookie=lnh.oidc这个cookie。然后清除第1步中设置的名为nonce的cookie。
最后跳转到客户端指定的地址(这个地址信息被保存在第1步中传递给oidc-server.dev的state参数中,被oidc-server.dev原样返回了这个信息)。然后读取用户信息如下(这里读取的是id_token中的完整信息):
其他的客户端登录
登录流程是和上面的步骤是一样的,一样会发起认证请求,区别在于已经登录的时候会在第4步直接返回post信息给客户端的地址,而不是打开一个登录页面,这里就不再详细介绍了。大家可以在本地运行一下,通过fiddler观察一下它们的请求流程。贴一下oidc-client-hybrid.dev这个客户端登录后的页面吧:
统一退出
退出的流程相比登录简单一些。如下图:
其中核心部分在于利用浏览器作为中间的媒介,来逐一的通知已经登录的客户端退出登录。
第1步:OIDC-Client - 触发登出请求
点击Logout链接。
点击退出后会触发一个GET请求,如下:
上图这个请求会返回一个302的响应,Location的地址指向oidc-server.dev的一个endsession的接口。同时会通过Set-Cookie来清除自身的cookie。
第2步:OIDC-Client - 登出请求
浏览器通过GET访问上一步中指定的Location地址。
接口地址定义在OIDC的发现服务中的end_session_endpoint字段中,参数信息定义在OIDC的Front-Channel-Logout规范中。
第3步:OIDC-Server - 验证登出请求
验证上图中传递的信息,如果信息无误则再一次重定向到一个地址(这里是IdentityServer4的实现机制,其实可以无需这个再次重定向的)。
第4步:OIDC-Server - 登出自身,返回包含IFrame的HTML
浏览器打开第3步中重定向的地址:
响应中会通过Set-Cookie(idsrv和idsrv.session)清除oidc-server.dev自身的登录状态。然后包含一个HTML表单页面,上图中iframe指向的地址是IdentityServer4内部维持的一个地址。访问这个地址后的信息如下:
1 <!DOCTYPE html>2 <html>3 <style>iframe{display:none;width:0;height:0;}</style>4 <body>5 <iframesrc='http://oidc-client-implicit.dev/oidc/front-channel-logout-callback?sid=b51ea235574807beb0deff7c6db6a381&iss=http%3A%2F%2Foidc-server.dev'></iframe>6 <iframesrc='http://oidc-client-hybrid.dev/oidc/front-channel-logout-callback?sid=b51ea235574807beb0deff7c6db6a381&iss=http%3A%2F%2Foidc-server.dev'></iframe>7 </body>8 </html>
上面代码中的iframe是真正的调用已经登录的客户端进行登出的地址(IdentityServer4会记录下来已经登录的客户端,没有登陆过的和没有配置启用Front-Channel-Logout的则不会出现在这里)。其中iframe指向的地址是OIDC客户端在oidc-server.dev中注册的时候配置的地址。参数则是动态附加上去的参数。
最后页面中包含一个js脚本文件,在页面load完成后,跳转到第2步中指定的post_logout_redirect_uri指向的回调页面。
第5步:OIDC-Client - 处理登出回调通知
在浏览器访问上面代码中iframe指向的地址的时候,被动登出的OIDC客户端会接收到登出通知。
响应中通过Set-Cookie(lnh.oidc)清除了需要被动登出的客户端的Cookie。至此,统一的登出完成。
总结
本文介绍了基于OIDC实现的SSO的工作原理和流程,但并未涉及到OIDC的具体实现IdentityServer4的是如何使用的(这部分通过读我提供的源码应该是很容易理解的),旨在解释一下如何用OIDC实现SSO,而非如何使用OIDC的某一个实现框架。OIDC是一个协议族,这些具体每一步怎么做都是有标准的规范的,所以侧重在了用HTTP来描述这个过程,这样这个流程也就可以用在java,php,nodejs等等开发平台上。
参考
本文源代码:https://github.com/linianhui/oidc.example
认证授权:http://www.cnblogs.com/linianhui/category/929878.html
Id Token:http://www.cnblogs.com/linianhui/p/openid-connect-core.html#auto_id_5
JWT:http://www.cnblogs.com/linianhui/p/oauth2-extensions-protocol-and-json-web-token.html#auto_id_5
数字签名:http://www.cnblogs.com/linianhui/p/security-based-toolbox.html#auto_id_16
OIDC:http://openid.net/connect/
IdentityServer4:https://github.com/IdentityServer/IdentityServer4
原文:http://www.cnblogs.com/linianhui/p/oidc-in-action-sso.html
.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com
基于OIDC(OpenID Connect)的SSO相关推荐
- jws webservice 跳过https认证_基于OAuth2的OIDC (OpenId Connect)身份认证
OIDC协议 OIDC(OpenID Connect)是在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议. OAuth2协议 OAuth2是一个授权协议,它无法提供完善的 ...
- 基于OIDC实现单点登录SSO、第三方登录
OIDC联合身份认证机制 背景概念 1 OIDC身份认证协议 2 基于OIDC实现SSO 2.1 统一登录 2.1.1 流程 2.1.2 RP相关接口 2.1.3 OP相关接口 2.2 统一登出 2. ...
- 使用OAuth 2 / OpenID Connect的SSO的Spring Boot 2本机方法
这篇文章是3篇系列文章的最后一部分,该系列文章探讨了如何为基于Spring Boot 2的应用程序启用OAuth2提供程序SSO. 3个帖子是: 引导兼容OpenID Connect的OAuth2授权 ...
- OpenID Connect:OAuth 2.0协议之上的简单身份层
OpenID Connect是什么?OpenID Connect(目前版本是1.0)是OAuth 2.0协议(可参考本人此篇:OAuth 2.0 / RCF6749 协议解读)之上的简单身份层,用 A ...
- 微服务openid等_使用OpenID Connect在Quarkus中保护微服务
微服务openid等 This is the fourth part in a series on building a microservice from the ground up with Qu ...
- 基于OIDC(OpenID Connect)的SSO(纯JS客户端)
在上一篇基于OIDC的SSO的中涉及到了4个Web站点: oidc-server.dev:利用oidc实现的统一认证和授权中心,SSO站点. oidc-client-hybrid.dev:oidc的一 ...
- [认证授权] 5.OIDC(OpenId Connect)身份认证授权(扩展部分)
[认证授权] 5.OIDC(OpenId Connect)身份认证授权(扩展部分) 原文:[认证授权] 5.OIDC(OpenId Connect)身份认证授权(扩展部分) 在上一篇[认证授权] 4. ...
- 基于IdentityServer4的OIDC实现单点登录(SSO)原理简析
# 写在前面 IdentityServer4的学习断断续续,兜兜转转,走了不少弯路,也花了不少时间.可能是因为没有阅读源码,也没有特别系统的学习资料,相关文章很多园子里的大佬都有涉及,有系列文章 ...
- OIDC(OpenId Connect)身份认证
1 什么是OIDC? 看一下官方的介绍(http://openid.net/connect/): OpenID Connect 1.0 is a simple identity layer on to ...
最新文章
- 为什么学习Python数据分析
- 深度学习多变量时间序列预测:卷积神经网络(CNN)算法构建时间序列多变量模型预测交通流量+代码实战
- 【Java】MD5字符串的加密解密
- html导航去下划线,纯CSS实现导航栏下划线跟随的示例代码
- 多目标机器学习_NIPS2018 - 用多目标优化解决多任务学习
- pb 修改数据窗口种指定字段位置_第三章 Python数据类型 容器
- vat可以退税吗_【涨知识】企业对外投资可以申请出口退税吗?
- PhpStorm11.0 配置在浏览器中打开文件
- java中包装类介绍
- 《天天数学》连载37:二月六日
- ad用户和计算机显示2000个,AD 用户属性userAccountControl的详细解释
- [线性筛][筛素数/筛约数个数]
- otf字体转ttf 并压缩
- 微弱信号检测matlab代码,微弱信号检测方法研究
- 曾经沧海难为水,除却巫山不是云。
- 具名插槽 非具名插槽
- 中国科技技术大学潘建伟计算机,中国研制量子计算机“九章” 比超级计算机快一百万亿倍...
- html5 单个按钮控制音乐播放,HTML5音频控制停止按钮(而不是暂停)
- html+word-break-all,强制换行word-break:break-all怎么用?
- Typecho的背景图片API
热门文章
- 《A Seat at the Table》作者访谈录
- memcached安装和php扩展memcache安装
- python assert的作用
- 这些Intel Atom处理器千万别升Windows 10创意者更新:不兼容
- [Windows]_[0基础]_[使用命令行工具dumpbin分析文件]
- java内存:堆、栈、常量池、方法区
- Android实现通用的ActivityGroup(效果类似Android微博客户端主界面),...
- 基于事件驱动架构构建微服务第15部分:SPA前端
- CSRobot gen:mssql-c#类型映射
- Apache ECharts顺利毕业,成为ASF顶级项目