DotNetOpenAuth 是 .Net 环境下OAuth 开源实现框架。基于此,能够方便的实现 OAuth 验证(Authorization)服务、资源(Resource)服务。针对 DotNetOpenAuth,近期打算整理出三篇随笔:html
DotNetOpenAuth Part 1 : OAuth2 Authorization 验证服务实现及关键源码解析服务器
DotNetOpenAuth Part 2 : OAuth2 Resource 资源服务实现及关键源码解析框架
DotNetOpenAuth Part 3 : OAuth2 Client 访问实现几关键源码解析async
本篇是这一系列的 Part 1。ide
OAuth Authorization 服务负责为用户下发 Access Token,然后用户用这一 Token 去请求资源和逻辑访问。Client 端发送来附带 ClientId 和 ClientSecret 的 request,Authorization 端验证请求的合法性,生成 Access Token 并下发,这就是OAuth 验证服务的主要职责。ui
【实现层面】this
基于 DotNetOpenAuth 开发 Authorization 服务的主要工做是实现 IAuthorizationServerHost 接口,而其中关键要实现的方法有两个,它们分别是 GetClient 和 CreateAccessToken。spa
1 IClientDescription GetClient(string clientIdentifier); .net
由 Client 端发送来的标示 clientIdentifier 获取该 Client 在验证服务端的详细信息,以备作 ClientSecret 比对验证。这里 ClientDescription 的来源能够是验证服务端DB,也能够是其余可提供该数据的外部服务。code
1 AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage);
基于策略,生成 AccessToken,并返回给请求验证的 Client。
方法实现示意
GetClient,
1 public IClientDescription GetClient(string clientIdentifier) 2 { 3 if (string.Equals(clientIdentifier, "iSun", StringComparison.CurrentCulture)) 4 { 5 var client = new Client() // Just initiate a Client instance, and in production it comes from DB or other service. 6 { 7 Name = "iSun", 8 ClientSecret = "1", 9 ClientTypeValue = 1 10 }; 11 12 return client; 13 } 14 15 throw new ArgumentOutOfRangeException("clientIdentifier"); 16 }
CreateAccessToken,
1 public AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage) 2 { 3 var accessToken = new AuthorizationServerAccessToken(); 4 5 accessToken.Lifetime = TimeSpan.FromMinutes(2); 6 accessToken.ResourceServerEncryptionKey = ... ... 7 accessToken.AccessTokenSigningKey = ... ... 8 9 var result = new AccessTokenResult(accessToken); 10 return result; 11 }
实现了 IAuthorizationServerHost 接口下的两个关键方法,下面要作的仅仅是使用该 AuthorizationServerHost 实例初始化 AuthorizationServer 类,并调用其 HandleTokenRequestAsync 方法。
1 private readonly AuthorizationServer authorizationServer = new AuthorizationServer(new iSunAuthorizationServerHost(AuthService.Configuration)); 2 3 public async Task<ActionResult> Token() 4 { 5 var response = await authorizationServer.HandleTokenRequestAsync(Request); 6 return response.AsActionResult(); 7 }
【原理层面】
实现层面的介绍就是这些,是否是很简单?但仅知道这些显然不够,你始终仍是不明 ... 但觉厉。DotNetOpenAuth Authorization 框架如何作到仅这两个方法就实现了验证服务呢?好在 DotNetOpenAuth 是开源项目,咱们研究一下源码就会豁然开朗。先上一张 Authorization 内部调用的示意图(点击打开大图)。
如图所示,调用始自 AuthorizationServer.HandleTokenRequestAsync,后依次经步骤1 - 9,终于 AuthorizationServerHost.CreateAccessToken 调用。其中步骤七、八、9是整个 Token 下发过程当中的关键方法调用。Step 7 GetClient 获取 request 端的详细信息,以后Step 8 IsValidClientSecret 验证 ClientSecret 合法性,若验证经过则Step 9 CreateAccessToken 并下发予请求的 Client 端。逻辑示意图以下(点击打开大图)。
关键步骤七、八、9源码片断以下:
调用 GetClient,并进行 ClientSecret Validation(DotNetOpenAuth.OAuth2.ChannelElements.ClientAuthenticationModule 中 TryAuthenticateClientBySecret 方法)。
1 protected static ClientAuthenticationResult TryAuthenticateClientBySecret(IAuthorizationServerHost authorizationServerHost, 2 string clientIdentifier, string clientSecret) { 3 Requires.NotNull(authorizationServerHost, "authorizationServerHost"); 4 5 if (!string.IsNullOrEmpty(clientIdentifier)) { 6 var client = authorizationServerHost.GetClient(clientIdentifier); // Step 7: GetClient returns IClientDescription 7 if (client != null) { 8 if (!string.IsNullOrEmpty(clientSecret)) { 9 if (client.IsValidClientSecret(clientSecret)) { // Step 8: Validate ClientSecret 10 return ClientAuthenticationResult.ClientAuthenticated; 11 } else { // invalid client secret 12 return ClientAuthenticationResult.ClientAuthenticationRejected; 13 } 14 }
15 }
16 }
17 }
ClientDescription.IsValidClientSecret 方法(DotNetOpenAuth.OAuth2.ClientDescription 中 IsValidClientSecret 方法)。
1 public virtual bool IsValidClientSecret(string secret) { 2 Requires.NotNullOrEmpty(secret, "secret"); 3 4 return MessagingUtilities.EqualsConstantTime(secret, this.secret); 5 }
调用 CreateAccessToken 代码片断,若 Client 验证未经过,则返回 InvalidRequest(line 23)(DotNetOpenAuth.OAuth2.AuthorizationServer 中方法 HandleTokenRequestAsync)。
1 AccessTokenRequestBase requestMessage = await this.Channel.TryReadFromRequestAsync<AccessTokenRequestBase>(request, cancellationToken); 2 if (requestMessage != null) { 3 // Step 9: Call AuthorizationServerHost.CreateAccessToken to generate Token 4 var accessTokenResult = this.AuthorizationServerServices.CreateAccessToken(requestMessage); 5 ErrorUtilities.VerifyHost(accessTokenResult != null, "IAuthorizationServerHost.CreateAccessToken must not return null."); 6 7 IAccessTokenRequestInternal accessRequestInternal = requestMessage; 8 accessRequestInternal.AccessTokenResult = accessTokenResult; 9 10 var successResponseMessage = this.PrepareAccessTokenResponse(requestMessage, accessTokenResult.AllowRefreshToken); 11 successResponseMessage.Lifetime = accessTokenResult.AccessToken.Lifetime; 12 ...... 13 responseMessage = successResponseMessage; 14 } else { 15 // Validation failed, return error with InvalidRequest 16 responseMessage = new AccessTokenFailedResponse() { Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest }; 17 }
【小结】
应用 DotNetOpenAuth 框架开发 OAuth 验证服务比较容易,但若是不了解其实现原理,开发过程当中难免心虚,遇到问题也不能快速解决。因此,不只知其然,知其因此然仍是很重要的。(此篇所示代码多为示意用途,若想下载Demo,参考资料2处有详细代码,我在本篇就不贴出了。须要 DotNetOpenAuth 源码和 Sample 的,可前往官网 http://www.dotnetopenauth.net/)
参考资料:
一、[OAuth]基于DotNetOpenAuth实现Client Credentials Grant http://www.cnblogs.com/dudu/p/oauth-dotnetopenauth-client-credentials-grant.html
二、DotNetOpenAuth实践之搭建验证服务器 http://www.cnblogs.com/idefav2010/p/DotNetOpenAuth.html