zoukankan      html  css  js  c++  java
  • DotNetOpenAuth Part 1 : Authorization 验证服务实现及关键源码解析

    DotNetOpenAuth 是 .Net 环境下OAuth 开源实现框架。基于此,可以方便的实现 OAuth 验证(Authorization)服务、资源(Resource)服务。针对 DotNetOpenAuth,近期打算整理出三篇随笔:

      DotNetOpenAuth Part 1 : OAuth2 Authorization 验证服务实现及关键源码解析

      DotNetOpenAuth Part 2 : OAuth2 Resource 资源服务实现及关键源码解析

      DotNetOpenAuth Part 3 : OAuth2 Client 访问实现几关键源码解析

    本篇是这一系列的 Part 1。

    OAuth Authorization 服务负责为用户下发 Access Token,而后用户用这一 Token 去请求资源和逻辑访问。Client 端发送来附带 ClientId 和 ClientSecret 的 request,Authorization 端验证请求的合法性,生成 Access Token 并下发,这就是OAuth 验证服务的主要职责。

    【实现层面】

    基于 DotNetOpenAuth 开发 Authorization 服务的主要工作是实现 IAuthorizationServerHost 接口,而其中关键要实现的方法有两个,它们分别是 GetClient 和 CreateAccessToken。

     1 IClientDescription GetClient(string clientIdentifier); 

    由 Client 端发送来的标示 clientIdentifier 获取该 Client 在验证服务端的详细信息,以备做 ClientSecret 比对验证。这里 ClientDescription 的来源可以是验证服务端DB,也可以是其他可提供该数据的外部服务。

     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 调用。其中步骤7、8、9是整个 Token 下发过程中的关键方法调用。Step 7 GetClient 获取 request 端的详细信息,之后Step 8 IsValidClientSecret 验证 ClientSecret 合法性,若验证通过则Step 9 CreateAccessToken 并下发予请求的 Client 端。逻辑示意图如下(点击打开大图)。

    关键步骤7、8、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/


    参考资料:

    1、[OAuth]基于DotNetOpenAuth实现Client Credentials Grant  http://www.cnblogs.com/dudu/p/oauth-dotnetopenauth-client-credentials-grant.html

    2、DotNetOpenAuth实践之搭建验证服务器  http://www.cnblogs.com/idefav2010/p/DotNetOpenAuth.html

    3、http://www.dotnetopenauth.net/

  • 相关阅读:
    Python全栈-第六课 学习笔记
    python基础 day27 装饰器函数classmethod、staticmethod
    python基础 day26 面向对象-super方法、封装、property装饰器、反射
    python基础 day25 面向对象-继承2
    python基础 day24 面向对象-继承1
    python基础 day23 面向对象2
    python基础 day22 面向对象
    python基础 day21 递归函数、shutil模块
    python基础 day20 re模块、带参数的装饰器
    python基础 day19 正则表达式
  • 原文地址:https://www.cnblogs.com/isun/p/5074506.html
Copyright © 2011-2022 走看看