授权与认证OAuth 2.0 和 OIDC 的异同点
Posted oceanweave
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了授权与认证OAuth 2.0 和 OIDC 的异同点相关的知识,希望对你有一定的参考价值。
OAuth 2.0
简单说,OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。
OAuth 的核心就是向第三方应用颁发令牌
令牌与密码
令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。
(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。
(2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。
(3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。
上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。
注意,只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。
OAuth 2.0
OAuth 即 Open Authorization ,开放授权。
OAuth 2.0 是一个授权标准协议,可以使第三方应用获得对资源服务的有限访问。
根据 OAuth 2.0 协议规范,定义了四个角色:
资源所有者(Resource Owner):能够授予对受保护资源访问权限的实体。例如应用的用户是资源的所有者,可以授权其他人访问他的资源。当资源所有者是一个人时,它被称为最终用户。 —— 就相当于 admin,控制授予 Access Token 何种权限
资源服务器(Resource Server):存储受保护资源的服务器,能够接受并使用访问令牌来响应受保护的资源请求。就是资源服务器接受 Access Token,然后验证它拥有的权限,最后返回对应的资源。这个资源服务器一般是应用本身。—— 验证 Access Token 和相应的权限,并返回资源
授权服务器(Authorisation Server):服务器向客户端(即应用)颁发访问令牌来验证资源所有者并获得授权。即负责颁发 Access Token 的服务器,例如 IDaaS 就是一个授权服务器。—— 向客户端发放 Access Token
客户端(Client):需要获取访问令牌以访问资源服务器的应用。经过授权后,授权服务器为客户端颁发 Access Token。后续客户端可以携带这个 Access Token 到资源服务器那访问用户的资源。—— 需要 Access Token 来获取资源
在 OAuth 2.0 中一个应用可能既是 Resource Server,也是 Client,具体是什么角色,取决于应用工作的场景。
概念可能有点难嚼,还请慢咽。
这四个角色一直在围绕着一个叫 Access Token 的东西在转圈圈。
Access Token 也就是访问令牌,它用于允许应用访问一个资源 API。用户认证授权成功后,授权服务器会签发 Access Token 给应用。应用后续需要携带 Access Token 访问资源 API,资源服务 API 会检验 Access Token 是否有权限访问,从而决定是否返回对应资源。
而 Access Token 本质上只是一串随机字符串,并不能从中获取到任何信息,检验 Access Token 的步骤还需要资源服务器将它转发到授权服务器上进行解析验证。
了解完 Access Token 之后,我们来关注一下客户端调用方是如何获取到它的,也就是授权模式的选择。
授权码模式(Authorization Code):适用于具有完整前后端的传统 Web 应用以及移动或桌面端应用。
隐式模式(Implicit):适用于没有后端的基于浏览器(javascript)的纯前端应用。
密码模式(Resource Owner Password Credentials):适用于资源服务器和客户端之间高度信任的情况下,例如自家应用使用自家的资源。
客户端凭证模式(Client Credentials):适用于没有前端参与,纯后端交互的情况,期间没有用户的参与,客户端自己就是资源所有者。
…我们重点关注授权码模式和客户端凭证模式,这两个是最常用的。
流程
OAuth2.0协议流程描述了四种角色之间的交互过程,如下图所示。
上面的序列图一共分为以下6个步骤:
(1)第三方应用请求资源所有者授权。
(2)资源所有者同意给第三方应用授权。
(3)第三方应用使用步骤2中获得的授权,向授权服务器申请令牌。
(4)授权服务器对第三方应用进行认证并确认无误后,同意发放令牌。
(5)第三方应用使用步骤4中发放的令牌向资源服务器申请获取资源。
(6)资源服务器确认令牌无误后,向第三方应用开放资源访问。
四种模式
OAuth 2.0 对于如何颁发令牌的细节,规定得非常详细。具体来说,一共分成四种授权类型(authorization grant),即四种颁发令牌的方式,适用于不同的互联网场景。
1. 隐式授权模式(Implicit Grant)
- 第一步:用户访问页面时,重定向到认证服务器。
- 第二步:认证服务器给用户一个认证页面,等待用户授权。
- 第三步:用户授权,认证服务器想应用页面返回Token
- 第四步:验证Token,访问真正的资源页面
2. 授权码授权模式(Authorization code Grant)
示例见 开发者谈 | OAuth 2.0 和 OIDC 协议的关系?(内含必看案例)
- 第一步:用户访问页面
- 第二步:访问的页面将请求重定向到认证服务器
- 第三步:认证服务器向用户展示授权页面,等待用户授权
- 第四步:用户授权,认证服务器生成一个code和带上client_id发送给应用服务器
然后,应用服务器拿到code,并用client_id去后台查询对应的client_secret - 第五步:将code、client_id、client_secret传给认证服务器换取access_token和
refresh_token - 第六步:将access_token和refresh_token传给应用服务器
- 第七步:验证token,访问真正的资源页面
3. 密码模式(Resource Owner Password Credentials Grant)
- 第一步:用户访问用页面时,输入第三方认证所需要的信息(QQ/微信账号密码)
- 第二步:应用页面那种这个信息去认证服务器授权
- 第三步:认证服务器授权通过,拿到token,访问真正的资源页面
优点:不需要多次请求转发,额外开销,同时可以获取更多的用户信息。(都拿到账号密码了)
缺点:局限性,认证服务器和应用方必须有超高的信赖。(比如亲兄弟?)
应用场景:自家公司搭建的认证服务器
4. 客户端凭证模式(Client Credentials Grant)
- 第一步:用户访问应用客户端
- 第二步:通过客户端定义的验证方法,拿到token,无需授权
- 第三步:访问资源服务器A
- 第四步:拿到一次token就可以畅通无阻的访问其他的资源页面。
这是一种最简单的模式,只要client请求,我们就将AccessToken发送给它。这种模式是最方便但最不安全的模式。因此这就要求我们对client完全的信任,而client本身也是安全的。
因此这种模式一般用来提供给我们完全信任的服务器端服务。在这个过程中不需要用户的参与。
OpenID Connect
讲完 OAuth 2.0 授权,来到 OIDC 认证协议。
如标题,OIDC 全称是 OpenID Connect,是一个基于 OAuth 2.0 的认证 + 授权(OAuth 2.0 提供的能力)协议。
OIDC 可以理解为 OAuth 2.0 的超集,在 OAuth 2.0 之上实现了更多的标准,例如定义了一系列的 EndPoint 。还有一些规范诸如 Session Management,Front-Channel Logout,Back-Channel Logout 等。
OIDC 之所以有认证的功能,是因为在 OAuth 2.0 颁发 Access Token 的基础上,多颁发了一个 ID Token(用户的身份凭证),和 Access Token 是一个随机字符串不同的是,ID Token 是一个 JWT Token 。
ID Token 自身包含了一些用户的基本信息,而且由于 JWT 的防篡改性,让客户端不需要再向授权服务器进行身份验证,就能直接用 ID Token 来进行身份验证。即使 ID Token 包含的用户信息不够,也可以调用 OIDC 定义的 UserInfo EndPoint(用户信息接口)来获取更多的用户信息。
OIDC 协议定义的名词和 OAuth 2.0 协议定义的稍微有些出入:
-
OpenID Provider ,简称 OP :相当于 OAuth 2.0 中的授权服务器,除了负责颁发 Access Token ,还会颁发 ID Token 。例如 IDaaS 就是一个 OP 。
-
Relying Party ,简称 RP :代指 OAuth 2.0 中的客户端。
-
End User ,简称 EU :即用户。
最后, OIDC 的授权流程与 OAuth 2.0 是一样的,主要区别在于 OIDC 授权流程中会额外返回 ID Token。
云原生下的 OIDC Provider 服务
IDaaS 的认证和授权使用了 OIDC/OAuth 2.0。说白了,要搭建 IDaaS 得先搭建一个授权服务器 OpenID Provider (更多时候称之为 OIDC Provider )。
但即便完全了解 OIDC 协议,自行实现一套完整的 OIDC Provider 依旧十分繁琐,好在云原生环境下孕育出了很多优秀的项目。本文会简单介绍一下 Go 语言生态下的 dexidp/dex 和 ory/hydra ,感兴趣的可以自行到其官网详细了解。
dexidp/dex
Dex 作为 CNCF 的一个 sandbox 项目,是一个具有可插拔连接器的 OIDC 和 OAuth 2.0 提供商。
它通过”连接器“的身份来充当其他身份提供商的门户,可以将身份验证推送到 LDAP 服务器、SAML 提供商或 GitHub、Google 和 Active Directory 等其他一些成熟的身份提供商中进行验证。客户端只需写一次认证逻辑就可以与 Dex 对接,然后 Dex 来处理特定后端的协议。
ory/hydra
ORY Hydra 是一个 OAuth 2.0 和 OpenID Connect 提供者。
特别一说的是 Hydra 所实现的 OAuth 2.0 协议并不依赖 Go 标准库提供的,而是自实现的 fosite。
另外 ORY Hydra 还提供了一个 5 分钟的快速搭建教程。
[认证授权] 5.OIDC(OpenId Connect)身份认证授权(扩展部分)
在上一篇[认证授权] 4.OIDC(OpenId Connect)身份认证授权(核心部分)中解释了OIDC的核心部分的功能,即OIDC如何提供id token来用于认证。由于OIDC是一个协议族,如果只是简单的只关注其核心部分其实是不足以搭建一个完整的OIDC服务的。本篇则解释下OIDC中比较常用的几个相关扩展协议,可以说是搭建OIDC服务必备的几个扩展协议(在上一篇中有提到这几个协议规范):
- Discovery:可选。发现服务,使客户端可以动态的获取OIDC服务相关的元数据描述信息(比如支持那些规范,接口地址是什么等等)。
- OAuth 2.0 Multiple Response Types :可选。针对OAuth2的扩展,提供几个新的response_type。
- OAuth 2.0 Form Post Response Mode:可选。针对OAuth2的扩展,OAuth2回传信息给客户端是通过URL的querystring和fragment这两种方式,这个扩展标准提供了一基于form表单的形式把数据post给客户端的机制。
- 会话管理:Session Management :可选。Session管理,用于规范OIDC服务如何管理Session信息;Front-Channel Logout:可选。基于前端的注销机制。
1 OIDC Discovery 规范
顾名思义,Discovery定义了一个服务发现的规范,它定义了一个api( /.well-known/openid-configuration ),这个api返回一个json数据结构,其中包含了一些OIDC中提供的服务以及其支持情况的描述信息,这样可以使得oidc服务的RP可以不再硬编码OIDC服务接口信息。这个api返回的示例信息如下(这里面只是一部分,更完整的信息在官方的规范中有详细的描述和解释说明:http://openid.net/specs/openid-connect-discovery-1_0.html):
相信大家都看得懂的,它包含有授权的url,获取token的url,注销token的url,以及其对OIDC的扩展功能支持的情况等等信息,这里就不再详细解释每一项了。
2 OAuth2 扩展:Multiple Response Types
在本系列的第一篇博客[认证授权] 1.OAuth2授权中解释OAuth2的授权请求的时候,其请求参数中有一个 response_type 的参数,其允许的值有 code 和 token 两个,在这两个的基础上,OIDC增加了一个新值 id_token (详细信息定义在http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html):
- code:oauth2定义的。用于获取authorization_code。
- token:oauth2定义的。用户获取access_token。
- id_token:OIDC定义的。用户获取id_token。
至此OIDC是支持三种类型的response_type的,不但如此,OIDC还允许了可以组合这三种类型,即在一个response_type中包含多个值(空格分隔)。比如当参数是这样的时候 response_type=id_token token ,OIDC服务就会把access_token和id_token一并给到调用方。OIDC对这些类型的支持情况体现在上面提到的Discovery服务中返回的response_types_supported字段中:
3 OAuth2 扩展:Form Post Response Mode
在oauth2的授权码流程中,当response_type设置为code的时候,oauth2的授权服务会把authorization_code通过url的query部分传递给调用方,比如这样“https://client.lnh.dev/oauth2-callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz”。
在oauth2的隐式授权流程中,当response_type设置为token的时候,oauth2的授权服务会直接把access_token通过url的fragment部分传递给调用方,比如这样“http://client.lnh.dev/oauth2-callback#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&expires_in=3600”;
在oauth2中,上面的两种情况是其默认行为,并没有通过参数来显示的控制。OIDC在保持oauth2的默认行为的基础上,增加了一个名为response_mode的参数,并且增加了一种通过form表单传递信息的方式,即form_post(详细信息定义在http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html)。OIDC服务对这个扩展的支持情况体现在上面提到的Discovery服务中返回的response_modes_supported字段中:
当reponse_mode设置为form_post的时候,OIDC则会返回如下的信息:
<html> <head><title>Submit This Form</title></head> <body onload="javascript:document.forms[0].submit()"> <form method="post" action="https://client.lnh.dev/oidc-callback"> <input type="hidden" name="state" value="DcP7csa3hMlvybERqcieLHrRzKBra"/> <input type="hidden" name="id_token" value="eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiw iYXVkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLC Jpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0OjkwMzEiLCJpYXQiOjEzNjM5M DMxMTMsImV4cCI6MTM2MzkwMzcxMywibm9uY2UiOiIyVDFBZ2FlUlRHVE1B SnllRE1OOUlKYmdpVUciLCJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0F NTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZCIsImF1dGhfdGltZSI6MTM2Mz kwMDg5NH0.c9emvFayy-YJnO0kxUNQqeAoYu7sjlyulRSNrru1ySZs2qwqq wwq-Qk7LFd3iGYeUWrfjZkmyXeKKs_OtZ2tI2QQqJpcfrpAuiNuEHII-_fk IufbGNT_rfHUcY3tGGKxcvZO9uvgKgX9Vs1v04UaCOUfxRjSVlumE6fWGcq XVEKhtPadj1elk3r4zkoNt9vjUQt9NGdm1OvaZ2ONprCErBbXf1eJb4NW_h nrQ5IKXuNsQ1g9ccT5DMtZSwgDFwsHMDWMPFGax5Lw6ogjwJ4AQDrhzNCFc 0uVAwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA"/> </form> </body> </html>
这是一个会在html加载完毕后,通过一个自动提交的form表单,把id_token,access_token,authorization_code或者其他的相关数据POST到调用方指定的回调地址上。
4 OIDC 会话管理
综合上篇提到的idtoken和前面的discovery服务以及针对oauth2的扩展,则可以让OIDC服务的RP完成用户认证的过程。那么如何主动的撤销这个认证呢(也就是我们常说的退出登录)?总结来说就是其认证的会话管理,OIDC单独定义了3个独立的规范来完成这件事情:
- Session Management :可选。Session管理,用于规范OIDC服务如何管理Session信息。
- Front-Channel Logout:可选。基于前端的注销机制。
- Back-Channel Logout:可选。基于后端的注销机制。
其中Session Management是OIDC服务自身管理会话的机制;Back-Channel Logout则是定义在纯后端服务之间的一种注销机制,应用场景不多,这里也不详细解释了。这里重点关注一下Front-Channel Logout这个规范(http://openid.net/specs/openid-connect-frontchannel-1_0.html),它的使用最为广泛,其工作的具体的流程如下(结合Session Management规范):
在上图中的2和3属于session management这个规范的一部。其中第2步中,odic的退出登录的地址是通过Discovery服务中返回的end_session_endpoint字段提供的RP的。其中还有一个check_session_iframe字段则是供纯前端的js应用来检查oidc的登录状态用的。
4567这四步则是属于front-channel logout规范的一部分,OIDC服务的支持情况在Discovery服务中也有对应的字段描述:
4567这一部分中重点有两个信息:
- RP退出登录的URL地址(这个在RP注册的时候会提供给OIDC服务);
- URL中的sessionid这个参数,这个参数一般是会包含在idtoken中给到OIDC客户端,或者在认证完成的时候以一个独立的sessionid的参数给到OIDC客户端,通常来讲都是会直接把它包含在IDToken中以防止被篡改。
5 总结
本篇博客介绍了OIDC的发现服务,OAuth2的两个扩展规范,以及OIDC管理会话的机制。至此则可以构成一个完整的认证和退出的流程。其中有一点需要特别注意,这个流程中用到的token是OIDC定义的IDToken,IDToken,IDToken(重要要的事情说三遍),而不是OAuth2中定义的Access Token,千万不要混淆这两者,它们是有着本质的区别的(这一点在[认证授权] 3.基于OAuth2的认证(译)和[认证授权] 4.OIDC(OpenId Connect)身份认证授权(核心部分)中都有解释)。
6 Example
笔者基于IdentityServer3和IdentitySever4(两者都是基于OIDC的一个.NET版本的开源实现)写的一个集成SSO,API访问授权控制,QQ联合登陆(作为OP)的demo:https://github.com/linianhui/oidc.example 。
参考
oidc : http://openid.net/connect/
oidc - discovery :http://openid.net/specs/openid-connect-discovery-1_0.html
oauth2 - multiple-response-types :http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
oauth2 - form-post-response-mode :http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
oidc - session-menagement :http://openid.net/specs/openid-connect-session-1_0.html
oidc - front-channel-logout :http://openid.net/specs/openid-connect-frontchannel-1_0.html
以上是关于授权与认证OAuth 2.0 和 OIDC 的异同点的主要内容,如果未能解决你的问题,请参考以下文章
[认证授权] 4.OIDC(OpenId Connect)身份认证授权(核心部分)