OIDC / OAuth2.0 是一种开放的标准,可以帮助应用程序安全地访问用户的资源,而无需将用户的凭据(如用户名和密码)暴露给应用程序,我们可以通过标准协议,建立集中的用户目录和统一认证中心,将内外部业务系统的登录认证统一到认证中心,实现集中化的管理,从而避免每套业务系统都要搭建一套用户体系所造成的管理侧不便及安全侧的风险。
本文将带各位详细了解 OAuth 2.0 & OIDC 及其授权模式。
OAuth 2.0 是一个授权框架,使应用程序能够获得对 HTTP 服务上用户帐户的有限访问权限,例如 Facebook 、GitHub 和 DigitalOcean 。它通过将用户认证委托给托管用户帐户的服务,并授权第三方应用程序访问用户帐户来实现。
OpenID Connect (OIDC) 是建立在 OAuth 2.0 框架之上的简单身份层。它在 OAuth 2.0 提供的授权的基础上添加了认证。
初看上面这段话你可能很难理解,这里用白话解释下,OAuth 2.0 在设计之初,是为了 API 安全的问题,它是一个授权协议,而 OIDC 则在 OAuth2.0 协议的基础上,提供了用户认证、获取用户信息等的标准实现,当然我们也可以理解获取用户信息也是一个 API ,用 OAuth 2.0 也没问题的,考虑到读者的感受,在这里不要过于纠结,只要记住 OIDC 是完全兼容 OAuth2.0 的,我们现在也推荐用 OIDC。
啥啥啥,写的这都是啥,小白看到这些脑袋都大了,我在这里重点说下 OIDC/OAuth2.0 协议交互时所参与的几个角色,等你对协议熟悉了,可以反过头来再看下相关的介绍,在接下来的授权模式介绍中,我们会结合这四个角色,介绍下不同授权模式的流程。
OAuth2.0 / OIDC 中定义了 2 种 Client 类型:
我们在这里解释下:
Confidential Clients 机密型应用:能够安全的存储凭证( client_secret ),例如有后端服务,你的前端是 Vue ,后台是 Java ,那么可以理解为机密性应用,因为你的后端能够安全的保存 client_secret ,而不会将 client_secret 直接暴露给用户,此时你可以使用授权码模式。
Public Clients 公共型应用:无法安全存储凭证( Client Secrets ),例如 SPA 、移动端、或者完全前后端分离的应用,应当使用授权码 + PKCE 模式
重点来啦!我们要了解授权模式,才能更好的针对系统类型进行授权模式的选型,避免由于授权模式选型不当所造成的开发工作增加和安全侧的漏洞。
授权模式
选型建议
授权码模式适合应用具备后端服务器的场景。授权码模式要求应用必须能够安全存储密钥,用于后续使用授权码换 Access Token 。授权码模式需要通过浏览器与终端用户交互完成认证授权,然后通过浏览器重定向将授权码发送到后端服务,之后进行授权码换 Token 以及 Token 换用户信息。
整体上,有以下流程:
流程图如下:
如果你的应用是一个 SPA 前端应用或移动端 App ,建议使用授权码 + PKCE 模式来完成用户的认证和授权。授权码 + PKCE 模式适合不能安全存储密钥的场景(例如前端浏览器) 。
我们解释下 code_verifier 和 code_challenge 。
code_verifier:在 [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" 范围内,生成 43-128 位的随机字符串。 code_challenge:则是对 code_verifier 通过 code_challenge_method 例如 sha256 转换得来的。
用大白话讲下就是在认证是用户携带的是加密后的 code_challenge ,在用户认证成功获取 Token 时,客户端证明自己的方式则是把 code_verifier 原文发送,认证中心收到获取 Token 请求时通过 code_verifier + code_challenge_method 进行转换,发现最终结果与 code_challenge 匹配则返回 Token ,否则拒绝。
整体上,有以下流程:
流程图如下:
Client Credentials 模式用于进行服务器对服务器间的授权( M2M 授权),期间没有用户的参与。你需要创建编程访问账号,并将 AK 、SK 密钥对交给你的资源调用方。
注意:Client Credentials 模式不支持 Refresh Token。
整体上,有以下流程:
流程图如下:
隐式模式适合不能安全存储密钥的场景(例如前端浏览器),不推荐此模式,建议采用其他模式。在隐式模式中,应用不需要使用 code 换 token ,无需请求 /token 端点,AccessToken 和 IdToken 会直接从认证端点返回。
注意:因为隐式模式用于不能安全存储密钥的场景,所以隐式模式不支持获取 Refresh Token。
整体上,有以下流程:
流程图如下:
不推荐使用此模式,尽量使用其他模式。只有其他模式都无法解决问题时才会考虑使用密码模式。如果使用密码模式,请确保你的应用代码逻辑非常安全,不会被黑客攻击,否则将会直接泄露用户的账密。一般用于改造集成非常古老的应用,否则绝对不要把它作为你的第一选择。
整体上,有以下流程:
流程图如下:
对于一些连接到互联网的输入受限设备,设备不会直接验证用户身份,而是让用户通过链接或二维码转到手机或电脑上进行认证,从而避免了用户无法轻松输入文本所带来的糟糕体验。
Device Code Flow 这个与前面几个不太一样,开始不再是由资源持有者发起,而是由客户端开始。甚至登录的方法与客户端还没有特别的关联。
大致流程说明如下:
流程图如下:
一个设计良好的系统,都需要一个标准、安全、可扩展的用户认证协议,无论是 ToC 还是 ToE ,接来下我们还会针对具体的授权模式结合实际场景讲一下具体的模式。
本章我们介绍了 OIDC 和 OAuth2.0 两个协议,在使用起来,这两个协议并没有太大的区别,OAuth2.0 是一个授权协议,OIDC 协议则是在 OAuth2.0 的提供的授权的基础上添加了认证。
授权模式,我们分别介绍了授权码模式( Authorization Code ) 、授权码 + PKCE 模式( Authorization Code With PKCE ) 、客户端凭证模式( Client Credentials ) 、隐式模式( Implicit )(不推荐)、密码模式( Password ) (不推荐)、设备代码模式( Device Code )(几乎用不到) 。
授权模式选择