asp.net core系列 74 Identityserver4 授权许可类型应用场景分析
一.概述
在项目中做集成登录时,会使用到Identityserver4,在查看Identityserver4 官方文档后,会觉得文档介绍的不够全面和详细,这是因为它是基于oauth2.0和OpenID connect技术之上造轮子。要明白授权许可类型还需对oauth2.0技术有所了解,而ids4能够实现所有标准的授权许可类型。在ids4文档中也有介绍几个客户端的使用场景,那么再结合oauth2.0在全面掌握下授权许可类型应用场景分析。
授权许可类型客户端有多种应用场景包括:原生应用、web应用、浏览器应用(单页面Javascript应用)。
(1)web应用:oauth客户端最初的应用场景就是web应用,是运行在远程服务器上,需要通过web浏览器访问,应用的配置和运行时状态由web服务器维护,通常使用会话cookie与浏览器保持连接,这类应用能充分利用前端信道和后端信道两种信道方式,web应用很容易有效的使用授权码,客户端凭据,或者断言许可流程,由于浏览器一般不会将请求url中的片断部分发送给服务器,大多数情况下隐式许可流程不适用于web应用(但ids4中mvc客户端是web应用,示例使用了隐式许可流程)。
(2)浏览器应用:浏览器应用完全运行在浏览器内,一般使用javascript(单页面应用程序)。虽然应用的代码确实需要由web服务器提供,但代码本身不在服务器上运行,web服务器也不会维护应用的任何运行时状态。这类很容易使用前端信道,通过http重定向将用户转至另一页面。但使用后端信道就有些复杂,因为浏览器应用受限于安全限制,防止跨站攻击,因此这类应用适合隐式许可流程。
(3)原生应用:直接在最终用户的设备(计算机或者移动设备)上运行的应用。如控制台程序,移动开发,win开发等这类应用很容易使用后端信道,直接向远程服务器发送http请求。要使用前端信道会有些困难。
1.1 oauth2.0授权许可类型
授权许可类型指定客户端如何与令牌服务交互,也叫授权流程(Oauth flow)。在oauth2.0中授权许可类型包括:隐式许可类型、授权码许可类型、客户端凭据许可类型、资源拥有者凭据许可类型、断言许可类型。
1.2 ids4授权许可类型
IdentityServer支持所有oauth 标准授权类型,但对于常见的应用程序场景,您实际上只需要知道其中两种即可(code,ClientCredentials)。参考:https://identityserver4.readthedocs.io/en/latest/topics/grant_types.html 在ids4中授权许可类型配置是授权服务器Client.AllowedGrantTypes属性中,是一个集合类型,表示一个客户端可以支持多种授权许可类型,如下所示:
public List<ClientGrantType> AllowedGrantTypes { get; set; }
下面是ids4支持的授权许可类型:
1.3 通信信道
在了解授权许可类型之前,必须要了解通信信道,包括:前端信道和后端信道。这里的信道是指oauth中角色与组件之间的交互方式,oauth是一个基于htttp的协议,但是oauth中的交互并不总是通过简单的http请求和响应来完成。
后端信道: 后端信道通信是指http的请求和响应使用常规的http机制来通信:头部,查询参数,http方法。类似于使用httpclient发送请求一样。授权服务器提供了一个授权端点 /authorize,供客户端请求访问令牌和刷新令牌,客户端直接向该端点发出请求,携带一组表单格式的参数,授权服务器解析并处理这些参数,然后授权服务器返回一个代表令牌的json对象。
前端信道:这是一种间接通信方法,而不是后端信道的直接通信方法,它将web浏览器作为媒介,使用http请求实现两个系统间的间接通信。实现了跨安全域工作,信息隔离(身份认证在授权服务器如:OpenID connect)。前端信道需要用到web浏览器和http重定向,是交互式认证授权常用的方法,如企业网使用微信或支付宝登录,都是通过前端信道。 由于使用web浏览器数据可能被篡改,OpenID connect要求客户端或者授权服务器对前端信息中消息签名,通过这样增加安全机制。
二. 授权许可类型
2.1 Implicit
Implicit:隐式许可类型。完全运行在浏览器中的javascript应用就属于这种,是基于浏览器的应用,只使用前端信道与授权服务器通信。客户端向授权服务器的授权端点发送请求,通知授权服务器直接生成令牌返回。所有令牌都通过浏览器传输,不可用于获取刷新令牌,因为浏览器内的应用具有短暂性的特点。这种许可类型假设资源拥有者一直在场,必要时可以对客户端重新授权。客户端response_type值为token。
如下所示:授权服务器白名单中配置了一个可交互式的client,使用隐式许可类型。
// OpenID Connect implicit flow client (MVC) new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit, // where to redirect to after login RedirectUris = { "http://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } }
2.2 ClientCredentials
ClientCredentials:客户端凭据许可类型。这种不需要基于浏览器应用,客户端代表自己,只能使用后端信道,如使用控制台程序的HttpClient类get或post请求来与授权服务器直接通信。这种客户端需要提供ClientId(用户名)和ClientSecret (密码)来获取访问令牌。客户端凭据许可流程中不会颁发刷新令牌,因为客户端通过用户名和密码能够随时获取新令牌。
如下所示:客户端获取令牌的代码:
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" });
2.3 ResourceOwnerPassword
ResourceOwnerPassword:资源拥有者凭据许可类型。客户端使用纯文本的用户名和密码发送到授权服务器,授权服务器收到请求取出用户名和密码,并与本地存储的用户信息对比,如果匹配则授权服务器向客户端颁发令牌,这种叫密码流程,只能通过后端信道通信。这种密码流程是资源拥有者直接与客户端交互,而不是授权服务器,客户端能够存储密码并随意使用。如果可以避免,请不要在生产中使用它,这种许可类型只能作为过度方案,用于那些原本就是直接索取用户名和密码登录的客户端现转入到oauth。应尽快将这样的客户端转到授权码许可流程上来。这种许可类型还可以生成刷新令牌,为客户端提供刷新令牌之后,它就不需要再保存用户的密码了。
如下所示:客户端将以某种方式收集用户的密码,并在令牌请求期间将其发送到令牌服务。
var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = disco.TokenEndpoint, ClientId = "ro.client", ClientSecret = "secret", UserName = "alice", Password = "password", Scope = "api1" });
2.4 Hybrid
Hybrid:混合流许可类型。在Implicit隐式流中,所有令牌都是通过浏览器传输的,这对于身份令牌是完全可以的。现在,我们还想请求访问令牌。访问令牌比身份令牌更敏感,该流程使我们两全其美,身份令牌是通过浏览器通道传输的,因此客户端可以在进行更多工作之前对其进行验证。如果验证成功,则客户端将打开通向令牌服务的反向通道以检索访问令牌。客户端提供了访问offline_access
范围的权限-这允许为长期的API访问请求刷新令牌。
如下所示:授权服务器白名单配置,与隐式许可类型差不多,添加了对api资源的访问。
new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Hybrid, ClientSecrets = { new Secret("secret".Sha256()) }, RedirectUris = { "http://localhost:5002/signin-oidc" }, PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" }, AllowOfflineAccess = true };
参考: https://identityserver4.readthedocs.io/en/latest/quickstarts/5_hybrid_and_api_access.htm
SaveTokens:mvc客户端的属性,设置为true, 那么OpenID Connect处理程序会自动为您保存令牌在cookie中。
2.5 Code
authorization code授权码许可类型。客户端将用户代理重定向到授权服务器端点,用户登录页,带上redirect_uri,在重定向到授权端点 在identityserver4文档中,Code类型是用在了javascriptp客户端项目中,由于是用ajax来调用api, 需要在Web API项目中配置CORS,允许javascriptp客户端的ajax来访问。支持刷新令牌。
(1) 通过浏览器执行的前端通道步骤,其中发生所有“交互”的事情,例如登录页面,同意等。此步骤生成代表前端通道操作结果的授权代码。
(2) 反向通道步骤,其中步骤1中的授权代码与请求的令牌交换。此时,密钥客户端需要进行身份验证。
三. 选择正确的许可类型指引图