基于OIDC实现单点登录SSO、第三方登录[通俗易懂]

基于OIDC实现单点登录SSO、第三方登录[通俗易懂]OIDC联合身份认证机制背景概念1OIDC身份认证协议2基于OIDC实现SSO2.1统一登录2.1.1流程2.1.2RP相关接口2.1.3OP相关接口2.2统一登出2.2.1流程2.2.2RP需要在向OP注册时提供2.2.3RP相关接口2.2.4OP相关接口2.3持续监视2.3.1流程2.3.2RP相关接口2.3.3OP相关接口3在OIDC的SSO中集成第三方登录(…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

背景概念

认证(Authentication)
认证是指应用软件(身份信息使用方)通过采用某种方法来确认当前请求的用户是谁。基于密码的认证过程可以细分为三步:
(1)认证服务器(身份信息提供方)从客户端获取用户账密。
(2)认证服务器将拿到的账密与数据库中保存的账密进行比较,确认正确后,生成用户身份信息。
(3)使用方从提供方处获取用户身份信息。

  • 当提供方与使用方能够共享数据库,不必跨网络和安全边界进行交互时,两个角色就合并了,完成前两步就能确认当前请求的用户是谁,所以只需考虑一个问题:【Q1】按照什么流程、格式能够安全可靠地把用户账密从客户端传递给认证服务器
  • 当提供方与使用方独立部署,必须跨网络和安全边界进行交互时,三步都完成后使用方才能确认当前请求的用户是谁,因此除了【Q1】之外,还有一个问题需要考虑:【Q2】按照什么流程、格式能够安全可靠地把用户身份信息从提供方传递给使用方

所有能让使用方确认当前用户是谁的方法都可以统称为认证机制,应用软件根据其部署情况、所处角色、面临的问题来具体选择。

单点登录(SSO)
Single Sign On,在多个相互信任的应用组成的系统中,用户只需在一个应用上登录一次,就可以访问系统中的所有应用。狭义的单点登录是指用户在己方应用中登录一次就能访问己方系统中的所有应用。广义的单点登录概念还涵盖了第三方登录:己方应用信任第三方应用,用户只需在第三方应用上登录一次,就可以访问己方应用。本文中的单点登录特指其狭义概念。

第三方登录
用户利用已有的第三方应用(IDP)账密,来快速完成己方应用的注册、登录。

授权(Authorization)
授权是指应用软件(资源提供方)通过采用某种方法来确认用户允许第三方应用(资源使用方)对他的保护资源做哪些操作。
由于资源提供方与资源使用方是两个独立部署的应用,必须跨网络和安全边界进行交互,则需考虑的问题是:
【Q3】按照什么流程、格式能够安全可靠地把用户授权决策从资源使用方传递给资源提供方。解决方案是OAuth 2.0。

OAuth 2.0是授权协议,它规定了一套传递用户授权决策的标准流程、格式。采用该流程的优点在于用户在享受第三方应用替自己操作的便利时,不必与其共享自己的账号密码,第三方应用使用的是一个短期有效的访问令牌,并且用户能够控制令牌权限范围,以及随时能够让令牌失效。具体流程是:用户告知授权服务器(QQ用户中心),自己允许客户机应用(某第三方在线PS应用)在其同意的权限范围内(读取某个指定相册),访问自己保存在资源服务器(QQ空间)中的数据资源(照片)。显然,授权服务器(QQ用户中心)必须先认证用户的身份,才会发放访问令牌给客户机应用(PS应用),客户机应用凭借此访问令牌就能从资源服务器(QQ空间)上访问用户数据资源(读取指定相册的照片)。

OAuth 2.0术语
Resource Owner:资源拥有者,即用户。
Client:客户机应用,用户数据资源的使用方,凭借授权凭证访问用户存储在资源服务器上的特定数据资源。
AS:Authentication Server,授权服务器,有能力对用户进行认证、发放访问令牌、并且被资源服务器信任的一方。
RS:Resource Server,资源服务器,用户数据资源的提供方。

详解OAuth 2.0授权协议(Bearer token)

OAuth 2.0与认证机制的联系
(1)OAuth 2.0规定了授权服务器必须要对用户进行认证,但它只是授权协议,它不关心授权服务器如何完成认证(即【Q1】),授权服务器自行选择认证机制。授权服务器可以在表单认证、Basic、Digest、Mutual等密码认证机制中进行选择,也可以选择非密码的认证机制,还可以将两者结合起来使用。

(2)从实现的角度讲,OAuth 2.0能够作为一种联合身份认证机制来使用,但是不适合,会导致一些问题。

由于:

  • OAuth 2.0授权流程比较安全可靠
  • 授权服务器这个角色有能力且必须要对用户进行认证,看起来很适合作为用户身份信息的提供方
  • 有时候客户机应用也需要使用用户身份信息

于是有人就想,能不能利用OAuth 2.0的授权流程把用户身份信息从授权服务器传递给客户机应用?即利用OAuth 2.0解决【Q2】,把它作为一种联合身份认证机制来使用。

能,Github就是这样做的,它利用OAuth 2.0向客户机应用同时提供用户认证、授权两项服务。但是,OAuth 2.0的设计本意是用来做授权的,直接拿来做用户认证会导致一些问题。虽然提供方(Github)能够通过自己做额外设计来解决这些问题,但是当每个提供方采用的额外设计不一致时,整个流程就不再统一规范了,这会给使用方带来不必要的负担。于是就产生了OIDC这个协议,它规定了一套标准的额外设计。

OIDC(OpenID Connect)是一个身份认证协议,它规定了一套把用户身份信息从授权服务器(身份提供方)传递给客户机应用(身份使用方)的标准流程、格式。

1 OIDC身份认证协议

OIDC(OpenID Connect)是关于如何使用OAuth 2.0的授权服务器为客户机应用提供用户认证服务,并把对应的身份信息传递给客户机应用的标准协议。OIDC = (Identity, Authentication) + OAuth 2.0。由于它基于OAuth 2.0,当你搭建了一个OIDC的服务后,就同时拥有了认证和授权两个功能。官网

中文版总结:

OIDC术语
EU:End User:一个人类用户。
RP:Relying Party ,用户身份信息的使用方(OAuth 2.0的客户机应用)。
OP:OpenID Provider,用户身份信息的提供方(OAuth 2.0的授权服务器),有能力对用户进行认证。OP既可以是己方系统中的一个应用(用户中心),也可以是第三方应用(IDP)。
IDP:Identity Provider,身份提供商,当OP是第三方应用时,一般称为IDP。

OIDC的优点在于:

  1. 使得身份认证可以作为一个独立的服务存在。
  2. 可以很方便的实现跨一级域名的SSO(把授权服务器作为己方系统中具有单独一级域名的的用户中心来使用)。
  3. 可以兼容OAuth 2.0、SAML、WS-Federation、Windows AD、手机短信验证码等众多类型的IDP,易于集成第三方登录(把授权服务器作为第三方登录的身份提供商来使用)。
  4. OIDC基于OAuth 2.0,可以在一个流程中同时完成授权(签发access token)和认证(签发id token)。
  5. 比较安全可靠,一些敏感接口均强制要求TLS,除此之外,得益于JWT,JWS,JWE的安全机制,使得一些敏感信息可以进行数字签名、加密和验证,进一步确保整个认证过程中的安全性。

举例

  • 用户中心:大型Web应用系统,通常有A、B、C等多个一级域名,并且会把授权逻辑、用户信息相关逻辑独立成一个服务,称为用户中心(域名为O),O不处理业务逻辑。
  • SSO:A、B、C等业务应用需要登录时,把用户登录请求转发给用户中心O处理。用户只需在O登录一次,A、B、C就都能访问了。此时,A、B、C等业务应用就是RP,用户中心O就是OP。
  • 对外提供第三方登录服务:用户中心O还可以向第三方应用G提供身份认证服务。此时,第三方应用G就是RP,用户中心O是其IDP。
  • 使用外部提供的第三方登录服务:用户中心O也可以作为中介方,允许用户利用已有的G应用账密来登录A、B、C。由于G负责对用户进行认证,O从G处获取用户信息,此时O就是RP,G是其IDP。然后,O将得到的用户信息与自有账号体系绑定,向A、B、C提供用户信息。此时,A、B、C等业务应用是RP,用户中心O就是OP。

2 基于OIDC实现SSO

通常,SSO包括统一登录和统一登出两个功能。涉及统一登录的标准文件是OIDC的核心协议,涉及统一登出的的标准文件是OIDC的三个扩展协议Front-Channel LogoutBack-Channel LogoutSession Management

2.1 统一登录

OP:OpenID Provider,用户身份信息的提供方,己方系统中的用户认证中心,域名:op.com。
RP:Relying Party ,用户身份信息的使用方,己方系统中的业务应用,,域名:rp.com。

本例共使用了3种认证机制:
(1)所谓【基于OIDC实现】是指作为身份使用方的RP采用了OIDC认证机制来确认当前用户是谁。
(2)OP作为授权服务器和身份提供方,需要验证用户身份,本例采用了表单认证机制。OAuth 2.0不指定,OP可以自行选择,例如可替换为Basic、Digest、Mutual认证。
(3)OP作为授权服务器,还需要验证客户机应用(RP)的身份,即确认当前请求来自于哪个客户机应用(RP),本例采用了Basic认证机制。OAuth 2.0列举了2种认证机制:Basic认证、POST请求体传参(表单认证属于这类),但是不建议使用后者,允许授权服务器自行选择其他认证机制。因此,可替换的选择为:Digest、Mutual认证。

本例还采用了基于cookie的会话管理机制,可替换的选择为:基于session、基于token、基于认证

2.1.1 流程

1、RP要求用户登录时,让用户选择登录方式,用户选择使用己方系统账密登录RP(即传参iss=oidc_op),表单提交时触发 GET rp.com/login?iss=oidc_op,它将OP的授权接口GET op.com/authorization和所需参数组装成完整的URI,返回303 Location=该URI,通过浏览器重定向。

2、OP授权接口GET op.com/authorization发现用户未登录(没有携带名为pyoidc的cookie),返回303,通过浏览器重定向到OP登录页面。用户输入账密,表单提交时触发OP验证账密接口 POST op.com/user_pass/verify
(1)如果账密错误,则仍然重定向到OP登录页面。
(2)如果账密正确,则重定向回OP授权接口(返回303 Location=步骤1的Location),并设置OP的会话状态(设置名为pyoidc的cookie)。

3、浏览器携带cookie再度进入OP授权接口GET op.com/authorization,发现有pyoidc这个cookie,确认其值正确后,认为用户已登录,于是执行授权逻辑,签发授权码,返回303 Location=redirect_uri(即RP在步骤1中通过查询参数redirect_uri提供的重定向接口),浏览器重定向到redirect_uri。

4、RP的redirect_uri接口收到授权码,在后台使用授权码向OP令牌接口请求访问令牌(access token)和身份令牌(id token),使用access token向OP用户详情接口请求用户详细信息。

2.1.2 RP相关接口

1、GET rp.com/login?iss=oidc_op:用户选择使用己方系统账密登录(iss=oidc_op),此接口负责将OP的授权接口和所需传参组装成完整的URI,通过浏览器重定向,即返回:

HTTP/1.1 303 See Other
Location: http://op.com/authorization
?redirect_uri=http://rp.com/code_flow/oidc_op
&scope=openid+profile+email+address+phone
&response_type=code
&nonce=cdYrYNLv6wBHlBmZjWxvrQmmD
&state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&client_id=EqAfEpR492It

传参:

  • redirect_uri:必选,用户登录成功后,OP回传授权码等信息给RP的接口。
  • scope:必选,申请获取的资源权限,必须包含openid,表明申请获取id token。
  • response_type:必选,希望OP采用哪种OAuth 2.0授权流程来响应,code代表授权码流程。
  • client_id:必选,RP在OP注册的client_id。
  • state:推荐,不透明字符串,当OP重定向到redirect_uri时,会原样返回给RP,用于防止CSRF、 XSRF。由于OP会原样返回此参数,可将state值与用户在RP登录前最后浏览的URI绑定,便于登录完成后将用户重定向回最后浏览的页面。
  • nonce:可选,不透明字符串,当OP返回id token时,id token中会原样包含此值,用于减少重播攻击。

2、GET rp.com/code_flow/oidc_op:重定向接口(Redirection Endpoint),即接口1中的传参redirect_uri,需实现:
(1)接收OP回传的code(授权码)、state等信息,使用授权码在后台向OP的令牌接口POST op.com/token请求获取access token和id token。其中,请求头Authorization字段通过Basic关键字传递RP在OP注册的client_id和client_secret。

POST op.com/token
Authorization: Basic cUZFeFZtQlE4blBZOjVjNWVkYjA2OTA2MTZjZGJkNGNmOWMwYjBlMjg3MWVkNjM2MzE2ZTliNjI1NWQzMDA2MDg3NGJm
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&redirect_uri=http://rp.com/code_flow/oidc_op
&client_id=qFExVmBQ8nPY
&state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&code=Z0FBQUFBQmVjdHFwR20wY0U5WnJ0MUxVQVhZUm1VR2UyeW14SHNLQjdxZS1fZkRhQkVkdkI0bmwyXzhDTlZ3NlVLUm51dGotRUV1TlVydG8wTjlMbEVNSjJ6ZkVnUjd4R3lsZG9pdkJNZUdRWTVkdlYzV1BEbndzUFZNWWxkY0dnVUVpaVRqaDBlaXh0dTZwbUJYQ2FmY0dabmxnY05tLVVWY2hlWXNueTM5ak5xMFdQSjgzaC1vVzhVTWsxWDZ5bWliVFRQM1IzbzR6d1dRb1RHOTF6RmI0QjFpWTdIdXNWQ0VUMjJ0cHdSRTV0UUlwWUdGMHVyUT0%3D

(2)接收OP令牌接口返回的access token和id token等信息。使用state、OP提供的签名秘钥等信息校验id token真实性。
(3)使用access token在后台向OP的用户详情接口GET op.com/userinfo发请求,获取用户详细信息。其中请求头Authorization字段使用Bearer关键字传递access token。

GET op.com/userinfo
Authorization: Bearer Z0FBQUFBQmVjdHFwcW1Xc08ybG9BaG5PRGNJSTR3alFjTjJEcEF4aVl3VDZCMW5OTmFhOXdZeUR2bm4tX2d5U0Y0Z0Y0T2tFMWFZQ2dTaG1MSmdjZjlrOHlHNml5SXQ5R2RqVGN5SE84WjNmSkFfZ3MycTZMZXlQYXd2Nm0wZXhiMVkzeVNORGtHRExIN2xYX0twbkJfVmg2Wks3bGwtOTVvbEV3ajZuQWVETVFDYm8tWlZTZC1SdjVNMHRJU3c2QlYtTFp0a2wtQkVBMWFwZDJqXzJmZkRVT1Fka2I2ck9nLWFWRzRTaGVHN0Y4ZkZobmNHZ0FCaz0=

(4)RP构建自身的会话状态(设置一个cookie,表明表明用户已在RP登录)。
(5)找出state绑定的URI,将用户重定向回登录前最后浏览的页面。

2.1.3 OP相关接口

1、POST op.com/registration:RP向OP进行注册的接口,OP返回client_id和client_secret给RP。
2、GET op.com/static/jwks.json:OP提供JWT签名秘钥的接口。
3、GET op.com/authorization:授权接口(Authorization Endpoint),需实现:
(1)接收并校验RP在查询参数中传入的redirect_uri、scope、response_type、client_id、state、nonce。如果校验失败,返回OIDC规定的错误响应。(本例将这个带有查询参数的完整URI称为authz_uri,后面会用到它)
(2)检查用户是否已在OP登录(检查名为pyoidc的cookie)。如果未登录,则重定向到OP登录页面GET op.com/login.html?authz_uri=...(在查询参数中传入authz_uri);如果已登录,则执行授权逻辑,将授权码等回传参数与RP提供的redirect_uri组装成完整URI,通过浏览器重定向,即返回:

HTTP/1.1 303 See Other
Location: http://rp.com/code_flow/oidc_op
?state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&scope=openid+profile+email+address+phone
&code=Z0FBQUFBQmVjc2ExRTJvZmpGN1FNTERzV0NQOUVieHRHZUdLdUd5V0dvZUMzTzliS3hjeUVpVUpxN21GWWhhaTlvalVyblVOVXI2XzJVZG1vZlctNkRNZlpTanpFM2hVSzdKaWliSDUxX1RhcW5pUk9ScTRMNW1icUh6WlJGamxIWkJhbmFnakhxbUdTenJpZVowQ3dEbDh5c3Z1ZDBpOWFGSXBtMHBRSk1IUFdPTVBxUmtzUnEzVHFCWDlYMDhXUkItWXVWczkwT0Fjb1M1bktIalZCUUdSd2p3b2lnUGZYazhEODdYekhnRTJVdzZjRkR3U01SUT0%3D
&iss=http%3A%2F%2Fop.com
&client_id=EqAfEpR492It

传参:

  • state:将RP传入的state原样返回。
  • code:OP签发的授权码。
  • scope:OP批准的资源权限。
  • iss:授权码的签发人,即OP的域名。
  • client_id:授权码的签发对象,即RP的client_id。

4、POST op.com/user_pass/verify:验证账密接口,用户在OP登录页面输入账密,表单提交时触发此接口。其入参为账密和authz_uri,负责进行表单认证:
(1)如果账密错误,则仍然重定向回OP登录页面。
(2)如果账密正确,则构建OP自身的会话状态(设置名为pyoidc的cookie),通过浏览器重定向到authz_uri,即返回:

HTTP/1.1 303 See Other
Set-Cookie: pyoidc="1584580277|BEWREbvcxoKRTD/6bin6mA==|eeu5GgtXvmGysKMmRDWISee85yZZolGc0zBgdQ==|y/NQfIBzH01PcdK+BzBPXA=="; expires=Thu, 19-Mar-2020 01:16:17 GMT; HttpOnly; path=/
Location: http://op.com/authorization
redirect_uri=http://rp.com/code_flow/oidc_op
&scope=openid+profile+email+address+phone
&response_type=code
&nonce=cdYrYNLv6wBHlBmZjWxvrQmm
&state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&client_id=EqAfEpR492It

5、POST op.com/token:令牌接口(Token Endpoint),需实现:
(1)校验RP在请求头Authorization字段通过HTTP Basic认证传入的client_id和client_secret。
(2)校验RP在请求体中传入的code、grant_type、state、redirect_uri、client_id等参数。如果校验失败,返回OIDC规定的错误响应
(3)如果都校验通过,则生成access token、id token并返回。

HTTP/1.1 200 OK
Content-Type: application/json

{ 
   
	"state": "DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU", 
	"scope": "openid profile email address phone", 
	"access_token":"Z0FBQUFBQmVjdWxLcFBWVnFzMmJ3ckcxa1ptM21kS1lDd01rY0hsVUk1WFlkUTk2MXlrN3BkdmpjTGx0VGxFeXg0SlNfVWExNldqeExRZ0RhdWt6b1Ewdy12aWlueTRMc29sc01WOHg1QXhxWjFiU1BBMmhGX2FQZnZhSUJmQVJ4eUphZ0xWRjk3M3M5MUZ0eTlVZEJxOXFtSGNuZlNaS3BqU0ZKSFBoZXE1dWdjOE13alZhLTh3Z1NVQ191V3p5a2tCSjRsT01fYVVFTV9DQmc5UFFVZnBiUmVHSXJvdzdVYzctMzJwaHVoWkFDem5MUmo4YnB2WT0=", 
	"token_type": "Bearer", 
	"id_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6InZPQnl2cjBRbTBfVUI4RFZwMkRjRVNUeWt0OHZ5bV9PS1F2VjBhRm1yQWMifQ.eyJpc3MiOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAyIiwgInN1YiI6ICIwMTFkYmUwMWFhYjZkZDQ1MWJlNDIzYmI0ZDI0MWFlOWM0OTA0YzJjN2FkOTk0Yzk4YmRhOGRkNmI3Y2VhYWYwIiwgImF1ZCI6IFsicTg5TFBjNkkwQTRqIl0sICJleHAiOiAxNTg0Njc1NTMwLCAiYWNyIjogInBhc3N3b3JkIiwgImlhdCI6IDE1ODQ1ODkxMzAsICJzaWQiOiAiZGNiN2E0ODlmM2JkNWY1Y2E0YmViZGFhYWY3ZjczMzY4ZjJkZTRhMDQ4YzYwNGUyMjBmOTRhNTQiLCAibm9uY2UiOiAiVzR6NldpUzF1djlwME1jMnJaNWc1Yng3In0.Jfvl4aCJy54YRbWobj7ozQUkfA2XezHtDwZhu7t5cNaguUuxNJ-epGTaub3DfmGcXI__CB_BXuQ-phWXqbz7YQ0jbwk6HtO6pGJCHfxGmcEHisM0z_-6BwJVrm6JbVw90m4zdmen5F_palkHyI4giYtrbNA8bIAraG-pZ5jZRJOmTIWHNGKopIHhUuzv39H1Ydgn5WROgz9lk24vHmyqiXiyCl2GXFcso6tEHtU9rM5oaGbIrZb6M0HfbxgmoagAw9Z9yG3p6DDihsiHUjWVccZ8o_IwS6NfJb16WFE2NoGlUBvv3Vt7VFoJJlNtTSjc7CMCij1p8k_FiN7nPMoq8w"
	}

6、GET op.com/userinfo:用户详情接口(UserInfo Endpoint),需实现:
(1)校验RP在请求头Authorization字段中通过Bearer关键字传入的access token。如果校验失败,返回OIDC规定的错误响应
(2)如果校验通过,返回用户详细信息。

2.2 统一登出

OIDC通过扩展协议提供了两种注销机制:Front-Channel LogoutBack-Channel Logout 。如果OP同时支持两种注销机制,RP在注册时任选一种即可。

2.2.1 流程

1、用户点击退出登录时,触发请求GET rp.com/logout,RP清除该用户的会话状态,重定向到OP的统一登出接口(End Session Endpoint)。该RP称为主动登出RP。
2、OP的统一登出接口GET op.com/end_session负责生成一个包含uid、sid、client_id、redirect_uri等信息的JWT,再重定向到OP的登出验证接口。
3、OP的登出验证接口GET op.com/logout_verify解析JWT,根据sid、client_id查找出所有该用户已登录的RP信息,逐一通知这些RP被动登出,同时清除该用户在OP的会话状态。最后重定向到主动登出RP提供的post_logout_redirect_uri,通知它用户已成功退出登录。
4、主动登出RP收到对post_logout_redirect_uri的请求时,返回退出成功的页面给用户。
6、被动登出RP收到对frontchannel_logout_uri或backchannel_logout_uri的请求时,清除该用户的会话状态。
7、当用户刷新被动登出RP的页面时,页面提示用户已退出登录。

2.2.2 RP需要在向OP注册时提供

1、post_logout_redirect_uri:必选,主动登出时接收OP成功登出通知的接口。
2、两种注销机制任选其一:

  • 如果采取frontchannel注销机制,则额外提供:
    (1)frontchannel_logout_uri:必选,被动登出时接收OP登出通知的接口。
    (2)frontchannel_logout_session_required:可选,值为True表示要求OP在回调frontchannel_logout_uri时提供传参iss、sid,否则不传。
  • 如果采取backchannel注销机制,则额外提供:
    (1)backchannel_logout_uri:必选,被动登出时接收OP登出通知的接口。
    (2)backchannel_logout_session_required:可选,值为True表示要求OP在回调backchannel_logout_uri时提供传参iss、sid,否则不传。

2.2.3 RP相关接口

1、GET rp.com/logout:用户点击退出登录时,触发此接口,需实现:
(1)清除该用户的会话状态(将RP指定cookie值设置为空)。
(2)将OP的统一登出接口和所需参数组装成完整的URI,通过浏览器重定向,即返回:

HTTP/1.1 303 See Other
Location: http://op.com/end_session?id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6InZPQnl2cjBRbTBfVUI4RFZwMkRjRVNUeWt0OHZ5bV9PS1F2VjBhRm1yQWMifQ.eyJpc3MiOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAyIiwgInN1YiI6ICIwMTFkYmUwMWFhYjZkZDQ1MWJlNDIzYmI0ZDI0MWFlOWM0OTA0YzJjN2FkOTk0Yzk4YmRhOGRkNmI3Y2VhYWYwIiwgImF1ZCI6IFsibWg2OVdDanR0YTVYIl0sICJleHAiOiAxNTg0NjkzMjgzLCAiYWNyIjogInBhc3N3b3JkIiwgImlhdCI6IDE1ODQ2MDY4ODMsICJzaWQiOiAiOWYxZTRhMjIwZDIwOGQwYWUzMTFmYzUxMzNlNTlhYzFmNDAyNjM0ZTA2ZDE2YjVmZTIwZmI2OTIiLCAibm9uY2UiOiAiSmEweXZESjFuQ1VjQjNwQk1xOXZvN09KIn0.HL56KJiE11oEZNWFai3BJczcmXHoPzizlfOTSl2q0cpUjKE9Y-Ku6O968Fbcm_fwLVTHkJBiSmmqv9DIGFa10SUuTl6Yc7rWUKgwUsIKavQ16OMBwX2XQ2TGGEq5DAodSpbdJegPfX9Q5mjk4-NfCW2aLC9fNJN-6wQtq8wZSDyc4pHUtkufioA5f114RMgMHtmg2_ws-gN1PN_twKsidzi8BFKHg2QRHUV_8-WxzS4nQPTvWIH4YdmesexZ8_VgmGQx6NtTqiVlNq_ojz50r1KaQ3lJ8PBG9X1iMxElRrjEE1ZH-4lQ9db_lDmVbCn-hYvt1QtbbYa9Oi641J0dHg
&post_logout_redirect_uri=http%3A%2F%2F127.0.0.1%3A8001%2Flogout_success%2Foidc_op

传参:

  • post_logout_redirect_uri:必选,当用户在OP成功登出后,OP会对主动登出RP回调此接口,以通知RP,必须与注册时提供的post_logout_redirect_uri一致。
  • id_token_hint:必选,RP将统一登录时收到的id token作为id_token_hint提供给OP,以告知OP是哪个用户要登出。
    (这两个参数是在OIDC扩展协议Session Management中规定的)

2、GET rp.com/logout_success:即RP提供的post_logout_redirect_uri, 当OP回调时,返回退出成功的页面给用户。
3、GET rp.com/frontchannel_logout_callback:采取frontchannel注销机制,RP被动登出时接收OP登出通知的接口,需实现:
(1)如果OP回调此接口时提供传参iss、sid,则校验。如果校验失败,返回OIDC规定的错误响应
(2)清除该用户的会话状态(将RP指定cookie值设置为空)。
4、POST rp.com/backchannel_logout_callback:采取backchannel注销机制,RP被动登出时接收OP登出通知的接口,需实现:
(1)接收OP通过请求体传入的参数logout_token,使用OP提供的签名秘钥等信息校验logout_token真实性。
(2)如果logout_token中包含了iss、sid,则校验。如果校验失败,返回OIDC规定的错误响应
(3)清除该用户的会话状态(将RP指定cookie值设置为空)。

2.2.4 OP相关接口

1、GET op.com/end_session:统一登出接口,负责生成一个包含uid、sid、client_id、redirect_uri等信息的JWT,重定向到OP的登出验证接口。
2、GET op.com/logout_verify:登出验证接口,需实现:
(1)接收并解析JWT,根据sid、client_id查找出所有该用户已登录的RP信息,逐一通知这些RP被动登出。

  • 如果RP注册时提供的是frontchannel_logout_uri,则构造带iframe的html,iframe的src=frontchannel_logout_uri(即OP让浏览器通知所有采取frontchannel注销机制的RP被动登出)。
  • 如果RP注册时提供的是backchannel_logout_uri,则在后台发POST请求给backchannel_logout_uri,携带logout_token(即OP自己通知所有采取backchannel注销机制的RP被动登出)

(2)清除该用户的会话状态(OP将名为pyoidc的cookie值设置为空)。
(3)无论RP采取frontchannel、还是backchannel注销机制,此接口最后返回的都是一个HTML,其中包含一个js脚本文件,在页面加载完成后,跳转到主动登出RP提供的post_logout_redirect_uri。

2.3 持续监视

OIDC的扩展协议Session Management规定了RP如何持续监视用户在OP登录状态的方法。此扩展协议既可以与两种注销机制分开使用,也可以结合使用。特别是对于没有服务端的纯JS应用,两种注销机制都无法使用,则可以通过此扩展协议提供的方法持续监视用户动态,实现被动登出。

2.3.1 流程

1、OP 的授权接口GET op.com/authorization在重定向到redirect_uri(GET rp.com/code_flow)并返回授权码等信息给RP的同时(即统一登录流程步骤3),额外提供session_state这个传参给RP,代表用户在OP的登录状态,OP同时设置一个名为pyoic_session的cookie用来提供信息给后面的op_iframe使用。

2、RP的redirect_uri(GET rp.com/code_flow)将收到的session_state保存起来,然后构造一个HTML作为响应返回,HTML中包含两个用于监视会话状态的iframe:rp_iframe(GET rp.com/session_iframe)和op_iframe(GET op.com/check_session_iframe),HTML中还包含一个js脚本文件,在页面加载完成后,跳转到用户登录前最后浏览的页面。

3、op_iframe(GET op.com/check_session_iframe),负责通过postMessage接收rp_iframe发来的消息,然后读取OP的pyoic_session这个cookie,提取所需信息,计算正确的session_state值,并与rp_iframe发来的session_state值进行比较。如果两者相同,则返回unchanged,否则返回changed。

4、rp_iframe负责按照设置的时间间隔使用 postMessage 发送指定消息轮询op_iframe,从而持续监视用户在OP的登录状态是否变化。如果从op_iframe收到的返回为unchanged,则继续监视。如果收到的是changed,则调用GET rp.com/session_change这个接口进行处理。

5、GET rp.com/session_change负责将OP 的授权接口与所需传参组装成完整的URI,与之前(即统一登录流程步骤1)的传参相比,额外提供了prompt和id_token_hint,通过浏览器重定向到此URI。

6、OP的授权接口GET op.com/authorization将照常响应该请求(统一登录流程步骤3),再次重定向到redirect_uri(GET rp.com/code_flow),此时GET rp.com/code_flow将做:
(1)如果收到的新id_token所代表的用户与原有id_token所代表的用户相同,表明用户在OP仍处于正常登录状态,则保存新的session_state值,重复步骤1-4,重新开始监视。
(2)反之,如果新id_token所代表的用户不同,或者没有收到新id_token等异常情况,则视同用户已在OP中退出登录,清除该用户在RP的会话状态。

2.3.2 RP相关接口

1、GET rp.com/code_flow:此接口在原功能(统一登录RP相关接口3)基础上,需额外实现:
(1)将session_state、client_id、issuer这三个信息单独保存起来,便于后续使用。
(2)构造一个HTML作为响应返回,HTML中包含两个隐藏的iframe:
rp_iframe:负责调用GET rp.com/session_iframe这个接口。
op_iframe:负责调用GET op.com/check_session_iframe这个接口。
(3)HTML中还包含一个js脚本文件,在页面加载完成后,跳转到用户登录前最后浏览的页面。
(4)当OP再次重定向到此接口(持续监视流程6),传入更新的id_token和session_state值时,对新的id_token进行校验,如果收到的新id_token所代表的用户与旧id_token所代表的用户相同,则保存新的session_state值。最后返回构造的HTML。
(5)反之,如果校验失败,或者新id_token所代表的用户不同,或者没有收到新id_token等异常情况,则应视同用户已在OP中退出登录,清除该用户在RP的会话状态(将RP指定cookie值设置为空)。

2、GET rp.com/session_iframe:获取rp_iframe的接口,需实现:
(1)找到已保存的session_state、client_id、issuer这三个信息。
(2)构造一个HTML作为响应返回,这个HTML就是rp_iframe,负责按照设置的时间间隔使用 postMessage 发送指定消息轮询op_iframe,从而持续监视用户在OP的登录状态是否变化。指定消息为var message = client_id + " " + session_state,如果从op_iframe收到的返回为unchanged,则继续监视。如果收到的是changed,则调用GET rp.com/session_change

3、GET rp.com/session_change:当rp_iframe检测到会话状态发生变化时,调用此接口进行处理,需实现:
(1)将OP的授权接口和所需传参组装成完整的URI,通过浏览器重定向,即返回:

HTTP/1.1 303 See Other
Location: http://op.com/authorization
?redirect_uri=http://rp.com/code_flow/oidc_op
&scope=openid+profile+email+address+phone
&response_type=code
&nonce=cdYrYNLv6wBHlBmZjWxvrQmmD
&state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&client_id=EqAfEpR492It
&prompt=none
&id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6InZPQnl2cjBRbTBfVUI4RFZwMkRjRVNUeWt0OHZ5bV9PS1F2VjBhRm1yQWMifQ.eyJpc3MiOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAyIiwgInN1YiI6ICIwMTFkYmUwMWFhYjZkZDQ1MWJlNDIzYmI0ZDI0MWFlOWM0OTA0YzJjN2FkOTk0Yzk4YmRhOGRkNmI3Y2VhYWYwIiwgImF1ZCI6IFsiNVRsMFhFbWNhZnBlIl0sICJleHAiOiAxNTg0NzE2MjkzLCAiYWNyIjogInBhc3N3b3JkIiwgImlhdCI6IDE1ODQ2Mjk4OTMsICJzaWQiOiAiYjg5NGRkZmZmMDgyOGY0YjRkMzg3NzE2MzcyY2ViZGVhZjg1ZjNkZWM3Nzk2OGExYjc2MTg1NjMiLCAibm9uY2UiOiAiSkhRS0w3Um0wZ2FCSW9oakpPRHZVcEZaIn0.Ma14vJMfTUBmQlG0vduKoUAjzMnhrQEn6FYTP5QQbAC4imJZWovqlgyijJpVbf9B3oevX4zc_i-19Stb7CdHVy9k_zuTYN7RRgo60Oz7k4WnAU-4NdmcmHD1MSHJUJpSLZcTPL81kc_YNGHHvxgOW217OOfF1yqfQADyvbwI4GNq05rq4R3mlkt3wdZGQaewJKC1qSMckaEAgtWbE4Iaj71uIoHuG_KHJdoY2CxSvbzBV55AN8soOL7kiPWfn6uIuDJVIFGTlL9ufRKogh7gDbKbUQ91q5yY1ARfP70z8LCnFI1ceLqJuJALa5FItRnayfO_RI2txVoGrWDyOd1F_g

额外提供的2个传参:

  • prompt=none,代表OP收到此请求时不用通过授权页面告知用户,此请求仅用于获取更新的id_token值和session_state值。
  • id_token_hint=RP统一登录时收到的的id_token值,用于告知OP想要获取哪个用户的新id_token值和session_state值。

2.3.3 OP相关接口

1、GET op.com/authorization:授权接口在原功能(统一登录OP相关接口3)基础上,需额外实现:
(1)额外提供session_state这个传参给RP,代表用户在OP的登录状态,它对RP来说是不透明的。
(2)设置一个名为pyoic_session的cookie用来提供信息给后面的op_iframe使用。

2、GET op.com/check_session_iframe:获取op_iframe的接口,需实现:
直接返回HTML作为响应,这个HTML就是op_iframe,负责通过postMessage接收rp_iframe发来的消息(client_id + " " + session_state),然后读取OP自身的pyoic_session这个cookie,提取所需信息,计算正确的session_state值,并与rp_iframe发来的session_state值进行比较。如果两者相同,则返回unchanged,否则返回changed。

3 在OIDC的SSO中集成第三方登录(GitHub)

上面的例子在统一登录时采用的是己方系统的账密。OIDC的优点之一是可以兼容众多的IDP(身份提供商),易于集成第三方登录,SSO的RP只需做出非常微小的改动。本例使用GitHub OAuth 2.0作为IDP,但是OIDC支持的IDP类型不局限于OAuth 2.0,它还支持SAML,WS-Federation,Windows AD,或者常用的手机短信验证码等。这是因为OIDC并不关心OP如何完成用户认证(【Q1】),它关心的只是如何把用户身份信息安全可靠地从OP传递给RP(【Q2】)。

本例涉及的认证机制:
(1)rp.com作为身份使用方,采用了OIDC认证机制,同上例。
(2)op.com作为授权服务器,需要验证客户机应用(rp.com)的身份,本例采用了Basic认证机制,同上例。
(3)op.com作为身份使用方(中介),采用了OIDC认证机制。
(3)GitHub作为身份提供方,需要验证用户身份,GitHub采用的是表单认证机制。
(4)GitHub作为授权服务器,需要验证客户机应用(op.com)身份,GitHub同时支持Basic、POST请求体传参两种认证机制,客户机应用(op.com)可以任选。

3.1 流程

1、RP要求用户登录时,让用户选择登录方式,用户选择使用Github登录(即传参iss=github),表单提交时触发 GET rp.com/login?iss=github,它将OP的授权接口和所需参数(RP的redirect_uri:GET http://rp.com/code_flow/oidc_op)组装成完整的URI(本例称其为authz_uri),通过浏览器重定向。

2、OP授权接口GET op.com/authorization发现用户未在OP登录(没有携带名为pyoidc的cookie),于是通过Github对用户进行身份认证,即将Github授权接口GET https://github.com/login/oauth/authorize和所需参数(OP的redirect_uri:GET op.com/github/verify)组装成完整的URI,通过浏览器重定向,由Github对用户进行身份认证。用户在Github登录并授权后,Github再通过浏览器重定向到OP的redirect_uri,同时提供code(授权码)和state。

3、OP的redirect_uri(GET op.com/github/verify)使用收到的授权码向Github令牌接口请求access token,使用access token向Github用户详情接口请求用户详细信息并绑定到自有账号体系上。至此用户认证通过,OP设置用户的会话状态(设置名为pyoidc的cookie),并重定向到authz_uri。

4、浏览器携带cookie重定向,再度进入OP授权接口GET op.com/authorization,发现用户已在OP登录(名为pyoidc的cookie校验通过),于是执行授权逻辑,签发OP授权码,重定向到RP的redirect_uri(RP在步骤1中提供的)。

5、RP的redirect_uri接收到OP的授权码,使用授权码向OP的令牌接口请求access token和id token,使用access token向OP的用户详情接口请求用户详细信息。

3.2 RP相关接口

1、GET rp.com/login?iss=github:传参iss=github表明用户选择使用Github第三方登录,此接口负责将OP的授权接口和所需参数组装成完整的URI,通过浏览器重定向,即返回:

HTTP/1.1 303 See Other
Location: http://op.com/authorization
?redirect_uri=http://rp.com/code_flow/oidc_op
&scope=openid+profile+email+address+phone
&response_type=code
&nonce=qku5JVdjlKjnbsF5hpRBJwdz
&state=c7HBU6Sb1nAcWELJx6l2aUsQfnwIl0EW
&client_id=EqAfEpR492It
&acr_values=github

额外传参:

  • acr_values:表明要求OP使用github作为IDP(身份认证提供商)。

由此可见,RP的改动非常小,只需一个参数(acr_values=github)就能实现Github登录,如果OP还支持微信登录,那么RP只需传递acr_values=wechat就够了。

3.3 OP相关接口

保存IDP(Github)的配置信息:
(1)授权接口(Authorization Endpoint):GET https://github.com/login/oauth/authorize
(2)令牌接口(Token EndPoint):POST https://github.com/login/oauth/access_token
(3)用户详情接口(UserInfo EndPoint):GET https://api.github.com/user
(4)client_id、client_secret:需手动向Github注册获取:https://github.com/settings/applications/new
(5)scope:确定需要向Github申请哪些资源权限。Github提供的可选scope

1、GET op.com/authorization:授权接口在原功能(统一登录OP相关接口3)基础上,需额外实现:
(1)找出传参acr_values指定的IDP(Github)的配置信息,将Github的授权接口和所需传参组装成完整的URI,通过浏览器重定向,即返回:

HTTP/1.1 303 See Other
Location: https://github.com/login/oauth/authorize
?redirect_uri=http://op.com/github/verify
&scope=public_repo+user
&state=c7HBU6Sb1nAcWELJx6l2aUsQfnwIl0EW
&client_id=9c21477eb0a5e2191342

传参:

  • redirect_uri:OP的redirect_uri:GET op.com/github/verify
  • client_id:OP在Github注册的client_id
  • scope:在Github提供的scope值中选择的
  • state:为了方便,传参state沿用了RP提供的state
  • 由于Github对于Web应用只支持授权码流程(authorization code grant type),所以它不要求提供response_type。

Github授权接口支持的传参
Github授权接口支持的传参
4、GET op.com/github/verify:即OP的redirect_uri,此接口需实现:
(1)接收GitHub回传的code(授权码)和state,并校验state值。
(2)使用授权码在后台向GitHub的令牌接口POST https://github.com/login/oauth/access_token请求access_token。

Github令牌接口支持的传参
Github令牌接口支持的传参
注意:

  • client_id、client_secret可以通过POST请求体传入,也可以通过请求头Authorization的HTTP Basic认证传入,GitHub令牌接口同时支持这两种方式。
  • GitHub采用的不是标准的OIDC协议,它的令牌接口只会返回access token,而不会返回id token。
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{ 
   "access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"public_repo,user", "token_type":"bearer"}

(3)使用access_token在后台向GitHub的用户详情接口GET https://api.github.com/user请求用户详细信息。通过请求头AuthorizationBearer关键字传递access token。
(4)将用户详细信息与自有账号体系绑定。

参考文档

  1. [认证 & 授权] 4. OIDC(OpenId Connect)身份认证(核心部分)
  2. [认证 & 授权] 5. OIDC(OpenId Connect)身份认证(扩展部分)
  3. OpenID Connect 协议入门指南
  4. 官方认证的OIDC实现库(C、C#、Java、JavaScript、Python、PHP、Ruby等)
  5. [OIDC in Action] 1. 基于OIDC(OpenID Connect)的SSO
  6. [OIDC in Action] 2. 基于OIDC(OpenID Connect)的SSO(纯JS客户端)
  7. [OIDC in Action] 3. 基于OIDC(OpenID Connect)的SSO(添加Github OAuth 2.0的支持)
  8. GitHub OAuth 第三方登录示例教程
  9. 浅谈SAML, OAuth, OpenID和SSO, JWT和Session
  10. 认证与授权——单点登录协议盘点:OpenID vs OAuth2 vs SAML
  11. JSON Web Token 入门教程
  12. OpenID Connect Federation 入门指南
  13. Web应用中基于密码的身份认证机制(表单认证、HTTP认证: Basic、Digest、Mutual)
  14. OAuth 2.0授权协议(Bearer token)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/179103.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号