<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      使用DotNetOpenAuth搭建OAuth2.0授權(quán)框架

      標(biāo)題還是一如既往的難取。

      我認(rèn)為對(duì)于一個(gè)普遍問(wèn)題,必有對(duì)應(yīng)的一個(gè)簡(jiǎn)潔優(yōu)美的解決方案。當(dāng)然這也許只是我的一廂情愿,因?yàn)楦鶕?jù)宇宙法則,所有事物總歸趨于混沌,而OAuth協(xié)議就是混沌中的產(chǎn)物,不管是1.0、1.0a還是2.0,單看版本號(hào)就讓人神傷。

      對(duì)接過(guò)各類(lèi)開(kāi)放平臺(tái)的朋友對(duì)OAuth應(yīng)該不會(huì)陌生。當(dāng)年我小試了下淘寶API,各種token、key、secret、code、id,讓我眼花繚亂,不明所以,雖然最終調(diào)通,但那種照貓畫(huà)虎的感覺(jué)頗不好受。最近公司計(jì)劃,開(kāi)放接口的授權(quán)協(xié)議從1.0升到2.0,這個(gè)任務(wù)不巧就落在了我的頭上。

      聲明:我并沒(méi)有認(rèn)真閱讀過(guò)OAuth2.0協(xié)議規(guī)范,本文對(duì)OAuth2.0的闡述或有不當(dāng)之處,請(qǐng)諒解。本文亦不保證敘述的正確性,歡迎指正。認(rèn)真的朋友可移步 http://tools.ietf.org/html/rfc6749

      OAuth2.0包含四種角色:

      • 用戶(hù),又叫資源所有者
      • 客戶(hù)端,俗稱(chēng)第三方應(yīng)用
      • 授權(quán)服務(wù)端,頒發(fā)AccessToken
      • 資源服務(wù)端,根據(jù)AccessToken開(kāi)放相應(yīng)的資源訪問(wèn)權(quán)限

      本文涉及到三種授權(quán)模式:

      • Authorization Code模式:這是現(xiàn)在互聯(lián)網(wǎng)應(yīng)用中最常見(jiàn)的授權(quán)模式。客戶(hù)端引導(dǎo)用戶(hù)在授權(quán)服務(wù)端輸入憑證獲取用戶(hù)授權(quán)(AccessToken),進(jìn)而訪問(wèn)用戶(hù)資源。需要注意的是,在用戶(hù)授權(quán)后,授權(quán)服務(wù)端先回傳客戶(hù)端授權(quán)碼,然后客戶(hù)端再使用授權(quán)碼換取AccessToken。為什么不直接返回AccessToken呢?主要是由于用戶(hù)授權(quán)后,授權(quán)服務(wù)端重定向到客戶(hù)端地址(必須的,用戶(hù)可不愿停留在授權(quán)服務(wù)端或者重新敲地址),此時(shí)數(shù)據(jù)只能通過(guò)QueryString方式向客戶(hù)端傳遞,在用戶(hù)瀏覽器地址欄中可見(jiàn),不安全,也有被前端惡意進(jìn)程截獲的風(fēng)險(xiǎn),于是分成了兩步。第二步由客戶(hù)端主動(dòng)請(qǐng)求獲取最終的令牌。
      • Client Credentials Flow:客戶(hù)端乃是授權(quán)服務(wù)端的信任合作方,不需要用戶(hù)參與授權(quán),事先就約定向其開(kāi)放指定資源(不特定于用戶(hù))的訪問(wèn)權(quán)限。客戶(hù)端通過(guò)證書(shū)或密鑰(或其它約定形式)證明自己的身份,獲取AccessToken,用于后續(xù)訪問(wèn)。
      • Username and Password Flow:客戶(hù)端被用戶(hù)和授權(quán)服務(wù)端高度信任,用戶(hù)直接在客戶(hù)端中輸入用戶(hù)名密碼,然后客戶(hù)端傳遞用戶(hù)名密碼至授權(quán)服務(wù)端獲取AccessToken,便可訪問(wèn)相應(yīng)的用戶(hù)資源。這在內(nèi)部多系統(tǒng)資源共享、同源系統(tǒng)資源共享等場(chǎng)景下常用,比如單點(diǎn)登錄,在登錄時(shí)就獲取了其它系統(tǒng)的AccessToken,避免后續(xù)授權(quán),提高了用戶(hù)體驗(yàn)。

        關(guān)于第四種隱式授權(quán)模式,乃是Authorization Code模式省略獲取授權(quán)碼的步驟,直接返回AccessToken,因此會(huì)帶來(lái)一定的安全隱患。不過(guò)在某些場(chǎng)景下還是合適的,比如瀏覽器插件和手機(jī)app,不會(huì)顯式呈現(xiàn)返回的url,如果不考慮惡意進(jìn)程截獲,那一定程度上還是安全的。this flow is not recommended and deprecated in OAuth 2.1

      上述模式涉及到三類(lèi)憑證:

      • AuthorizationCode:授權(quán)碼,授權(quán)服務(wù)端和客戶(hù)端之間傳輸。
      • AccessToken:訪問(wèn)令牌,授權(quán)服務(wù)端發(fā)給客戶(hù)端,客戶(hù)端用它去到資源服務(wù)端請(qǐng)求資源。
      • RefreshToken:刷新令牌,授權(quán)服務(wù)端和客戶(hù)端之間傳輸。

      對(duì)客戶(hù)端來(lái)說(shuō),授權(quán)的過(guò)程就是獲取AccessToken的過(guò)程。

      總的來(lái)說(shuō),OAuth并沒(méi)有新鮮玩意,仍是基于加密、證書(shū)諸如此類(lèi)的技術(shù),在OAuth出來(lái)之前,這些東東就已經(jīng)被大伙玩的差不多了。OAuth給到我們的最大好處就是統(tǒng)一了流程標(biāo)準(zhǔn),一定程度上促進(jìn)了互聯(lián)網(wǎng)的繁榮。

      我接到任務(wù)后,本著善假于物的理念,先去網(wǎng)上搜了一遍,原本以為有很多資源,結(jié)果只搜到DotNetOpenAuth這個(gè)開(kāi)源組件。更讓人失望的是,官方API文檔沒(méi)找到(可能是我找的姿勢(shì)不對(duì),有知道的兄弟告知一聲),網(wǎng)上其它資料也少的可憐,其間發(fā)現(xiàn)一篇OAuth2學(xué)習(xí)及DotNetOpenAuth部分源碼研究,欣喜若狂,粗粗瀏覽一遍,有收獲,卻覺(jué)得該組件未免過(guò)于繁雜(由于時(shí)間緊迫,我并沒(méi)有深入研究,只是當(dāng)前觀點(diǎn))。DotNetOpenAuth包含OpenID、OAuth1.0[a]/2.0,自帶的例子有幾處暗坑,不易(能)調(diào)通。下面介紹我在搭建基于該組件的OAuth2.0授權(quán)框架時(shí)的一些心得體會(huì)。

      本文介紹的DotNetOpenAuth乃是對(duì)應(yīng).Net4.0的版本。

      授權(quán)服務(wù)端


      授權(quán)服務(wù)端交道打的最多的就是客戶(hù)端,于是定義一個(gè)Client類(lèi),實(shí)現(xiàn)DotNetOpenAuth.OAuth2.IClientDescription接口,下面我們來(lái)看IClientDescription的定義:

      public interface IClientDescription {
      
          Uri DefaultCallback { get; }
      //0:有secret 1:沒(méi)有secret ClientType ClientType { get; }
      //該client的secret是否為空 bool HasNonEmptySecret { get; }
      //檢查傳入的callback與該client的callback是否一致 bool IsCallbackAllowed(Uri callback);
      //檢查傳入的secret與該client的secret是否一致 bool IsValidClientSecret(string secret); }

      其中隱含了許多信息。DefaultCallback表示客戶(hù)端的默認(rèn)回調(diào)地址(假如有的話),在接收客戶(hù)端請(qǐng)求時(shí),使用IsCallbackAllowed判斷回調(diào)地址是否合法(比如查看該次回調(diào)地址和默認(rèn)地址是否屬于同一個(gè)域),過(guò)濾其它應(yīng)用的惡意請(qǐng)求。若ClientType 為0,則表示客戶(hù)端需持密鑰(secret)表明自己的身份,授權(quán)服務(wù)端可以據(jù)此賦予此類(lèi)客戶(hù)端相對(duì)更多的權(quán)限,因此自定義的Client類(lèi)一般需要多定義一個(gè)ClientSecret屬性。DefaultCallback和ClientSecret在下文常有涉及。

      相關(guān)概念:timing attacks,官方例子在IsValidClientSecret方法中涉及到。個(gè)人覺(jué)得此處不需考慮,因?yàn)闆](méi)有為給方法單獨(dú)暴露接口出來(lái)。

      DotNetOpenAuth預(yù)定義了一個(gè)接口——IAuthorizationServerHost,這是個(gè)重要的接口,定義如下:

      public interface IAuthorizationServerHost
      {
          ICryptoKeyStore CryptoKeyStore { get; }
          INonceStore NonceStore { get; }
      
          AutomatedAuthorizationCheckResponse CheckAuthorizeClientCredentialsGrant(IAccessTokenRequest accessRequest);
          AutomatedUserAuthorizationCheckResponse CheckAuthorizeResourceOwnerCredentialGrant(string userName, string password, IAccessTokenRequest accessRequest);
          AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage);
          IClientDescription GetClient(string clientIdentifier);
          bool IsAuthorizationValid(IAuthorizationDescription authorization);
      }

      簡(jiǎn)單地說(shuō),CryptoKeyStore用于存取對(duì)稱(chēng)加密密鑰,用于授權(quán)碼和刷新令牌的加密,由于客戶(hù)端不需要對(duì)它們進(jìn)行解密,所以密鑰只存于授權(quán)服務(wù)端;關(guān)于AccessToken的傳輸則略有不同,關(guān)于這點(diǎn)我們待會(huì)說(shuō)。理解NonceStore 屬性需要知道Nonce和Timestamp的概念,Nonce與消息合并加密可防止重放攻擊,Timestamp是為了避免可能的Nonce重復(fù)問(wèn)題,也將一同參與加密,具體參看nonce和timestamp在Http安全協(xié)議中的作用;這項(xiàng)技術(shù)放在這里主要是為了確保一個(gè)授權(quán)碼只能被使用一次。CheckAuthorizeClientCredentialsGrant方法在客戶(hù)端憑證模式下使用,CheckAuthorizeResourceOwnerCredentialGrant在用戶(hù)名密碼模式下使用,經(jīng)測(cè)試,IsAuthorizationValid方法只在授權(quán)碼模式下被調(diào)用(授權(quán)碼換取AccessToken過(guò)程),這三個(gè)方法的返回值標(biāo)示是否通過(guò)授權(quán)。

      當(dāng)授權(quán)通過(guò)后,通過(guò)CreateAccessToken生成AccessToken并返回給客戶(hù)端,客戶(hù)端于是就可以用AccessToken訪問(wèn)資源服務(wù)端了。那當(dāng)資源服務(wù)端接收到AccessToken時(shí),需要做什么工作呢?首先,它要確認(rèn)這個(gè)AccessToken是由合法的授權(quán)服務(wù)端頒發(fā)的,否則,攻擊者就能使用DotNetOpenAuth另外建一個(gè)授權(quán)服務(wù)端,生成“合法”的AccessToken,后果可想而知。說(shuō)到身份認(rèn)證,最成熟的就是RSA簽名技術(shù),即授權(quán)服務(wù)端私鑰對(duì)AccessToken簽名,資源服務(wù)端接收后使用授權(quán)服務(wù)端的公鑰驗(yàn)證。我們還可以使用資源服務(wù)器公/私鑰對(duì)來(lái)加解密AccessToken(簽名在加密后),這對(duì)于OAuth2.0來(lái)說(shuō)沒(méi)任何意義,而是為OAuth1.0服務(wù)的(雖然https能保證傳輸過(guò)程加密安全性,但不保證瀏覽器端的安全性——用瀏覽器開(kāi)發(fā)者工具一看便知——需要應(yīng)用自己解決加密問(wèn)題。)。

      public AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage)
      {
          var accessToken = new AuthorizationServerAccessToken();
          int minutes = 0;
          string setting = ConfigurationManager.AppSettings["AccessTokenLifeTime"];
          minutes = int.TryParse(setting, out minutes) ? minutes : 10;//10分鐘
          accessToken.Lifetime = TimeSpan.FromMinutes(minutes);
      
          //這里設(shè)置加密公鑰
          //accessToken.ResourceServerEncryptionKey = new RSACryptoServiceProvider();
          //accessToken.ResourceServerEncryptionKey.ImportParameters(ResourceServerEncryptionPublicKey);
      
          //簽名私鑰,這是必須的(在后續(xù)版本中可以設(shè)置accessToken.SymmetricKeyStore替代)
          accessToken.AccessTokenSigningKey = CreateRSA();
      
          var result = new AccessTokenResult(accessToken);
          return result;
      }

      前面說(shuō)了,所有授權(quán)模式都是為了獲取AccessToken,授權(quán)碼模式和用戶(hù)名密碼模式還有個(gè)RefreshToken,當(dāng)然授權(quán)碼模式獨(dú)有Authorization Code。一般來(lái)說(shuō),這三個(gè)東西,對(duì)于客戶(hù)端是一個(gè)經(jīng)過(guò)加密編碼的字符串,對(duì)于服務(wù)端是可序列化的對(duì)象,存儲(chǔ)相關(guān)授權(quán)信息。需要注意的是客戶(hù)端證書(shū)模式?jīng)]有RefreshToken,這是為什么呢?我們不妨想想為什么授權(quán)碼模式和用戶(hù)名密碼模式有個(gè)RefreshToken,或者說(shuō)RefreshToken的作用是什么。以下是我個(gè)人推測(cè):

      首先要明確,AccessToken一般是不會(huì)永久有效的。因?yàn)椋珹ccessToken并沒(méi)有承載可以驗(yàn)證客戶(hù)端身份的完備信息,并且資源服務(wù)端也不承擔(dān)驗(yàn)證客戶(hù)端身份的職責(zé),一旦AccessToken被他人獲取,那么就有可能被惡意使用。失效機(jī)制有效減少了產(chǎn)生此類(lèi)事故可能造成的損失。當(dāng)AccessToken失效后,需要重新獲取。對(duì)于授權(quán)碼模式和用戶(hù)名密碼模式來(lái)說(shuō),假如沒(méi)有RefreshToken,就意味這需要用戶(hù)重新輸入用戶(hù)名密碼進(jìn)行再次授權(quán)。如果AccessToken有效期夠長(zhǎng),比如幾天,倒不覺(jué)得有何不妥,有些敏感應(yīng)用只設(shè)置數(shù)分鐘,就顯得不夠人性化了。為了解決這個(gè)問(wèn)題,引入RefreshToken,它會(huì)在AccessToken失效后,在不需要用戶(hù)參與的情況下,重新獲取新的AccessToken,這里有個(gè)前提就是RefreshToken的有效期(如果有的話)要比AccessToken長(zhǎng),可設(shè)為永久有效。那么,RefreshToken泄露了會(huì)帶來(lái)問(wèn)題嗎?答案是不會(huì),除非你同時(shí)泄露了客戶(hù)端身份憑證。需要同時(shí)具備RefreshToken和客戶(hù)端憑證信息,才能獲取新的AccessToken,我們甚至可以將舊的AccessToken當(dāng)作RefreshToken。同理可推,由于不需要用戶(hù)參與授權(quán),在客戶(hù)端證書(shū)模式下,客戶(hù)端在AccessToken失效后只需提交自己的身份憑證重新請(qǐng)求新AccessToken即可,根本不需要RefreshToken。

      授權(quán)碼模式,用戶(hù)授權(quán)后(此時(shí)并不返回AccessToken,而是返回授權(quán)碼),授權(quán)服務(wù)端要保存相關(guān)的授權(quán)信息,為此定義一個(gè)ClientAuthorization類(lèi):

      public class ClientAuthorization
      {
          public int ClientId { get; set; }
      
          public string UserId { get; set; }
      
          public string Scope { get; set; }
      
          public DateTime? ExpirationDateUtc { get; set; }
      }

      ClientId和UserId就不說(shuō)了,Scope是授權(quán)范圍,可以是一串Uri,也可以是其它標(biāo)識(shí),只要后臺(tái)代碼能通過(guò)它來(lái)判斷待訪問(wèn)資源是否屬于授權(quán)范圍即可。ExpirationDateUtc乃是授權(quán)過(guò)期時(shí)間,即當(dāng)該時(shí)間到期后,需要用戶(hù)重新授權(quán)(有RefreshToken)也沒(méi)用,為null表示永不過(guò)期。

      資源服務(wù)端


      在所有的授權(quán)模式下,資源服務(wù)端都只專(zhuān)注一件和OAuth相關(guān)的事情——驗(yàn)證AccessToken。這個(gè)步驟相對(duì)來(lái)說(shuō)就簡(jiǎn)單很多,以Asp.net WebAPI為例。在此之前建議對(duì)Asp.net WebAPI消息攔截機(jī)制不熟悉的朋友瀏覽一遍ASP.NET Web API之消息[攔截]處理。這里我們新建一個(gè)繼承自DelegatingHandler的類(lèi)作為例子:

      public class BearerTokenHandler : DelegatingHandler
      {
          /// <summary>
          /// 驗(yàn)證訪問(wèn)令牌合法性,由授權(quán)服務(wù)器私鑰簽名,資源服務(wù)器通過(guò)對(duì)應(yīng)的公鑰驗(yàn)證
          /// </summary>
          private static readonly RSAParameters AuthorizationServerSigningPublicKey = new RSAParameters();//just a 例子
      
          private RSACryptoServiceProvider CreateAuthorizationServerSigningServiceProvider()
          {
              var authorizationServerSigningServiceProvider = new RSACryptoServiceProvider();
              authorizationServerSigningServiceProvider.ImportParameters(AuthorizationServerSigningPublicKey);
              return authorizationServerSigningServiceProvider;
          }
      
          protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
          {
              if (request.Headers.Authorization != null)
              {
                  if (request.Headers.Authorization.Scheme == "Bearer")
                  {
                      var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(this.CreateAuthorizationServerSigningServiceProvider(), null));
                      var principal = resourceServer.GetPrincipal(request);//可以在此傳入待訪問(wèn)資源標(biāo)識(shí)參與驗(yàn)證
                      HttpContext.Current.User = principal;
                      Thread.CurrentPrincipal = principal;
                  }
              }
      
              return base.SendAsync(request, cancellationToken);
          }
      }

      需要注意,AccessToken乃是從頭信息Authorization獲取,格式為“Bearer:AccessToken”,在下文“原生方式獲取AccessToken”中有進(jìn)一步描述(OAuth2.0引入了 Bearer 和 MAC 兩種驗(yàn)證機(jī)制, Bearer 使用更簡(jiǎn)單,但需要 TLS, MAC 可以走 HTTP, 與 OAuth 1.0a 更接近)。ResourceServer.GetPrincipal方法使用授權(quán)服務(wù)端的公鑰驗(yàn)證AccessToken的合法性,同時(shí)解密AccessToken,若傳入?yún)?shù)有scope,則還會(huì)判斷scope是否屬于授權(quán)范圍內(nèi),通過(guò)后將會(huì)話標(biāo)識(shí)賦給當(dāng)前會(huì)話,該會(huì)話標(biāo)識(shí)乃是當(dāng)初用戶(hù)授權(quán)時(shí)的用戶(hù)信息,這樣就實(shí)現(xiàn)了用戶(hù)信息的傳遞。一般來(lái)說(shuō)若返回的principal為null,就可以不必執(zhí)行后續(xù)邏輯了。

      客戶(hù)端


      可以認(rèn)為DotNetOpenAuth.OAuth2.Client是DotNetOpenAuth給C#客戶(hù)端提供的默認(rèn)SDK。我們以授權(quán)碼模式為例。先聲明一個(gè)IAuthorizationState接口對(duì)象,IAuthorizationState接口是用來(lái)保存最終換取AccessToken成功后授權(quán)服務(wù)端返回的信息,其部分定義如下:

      public interface IAuthorizationState {
          Uri Callback { get; set; }
          string RefreshToken { get; set; }
          string AccessToken { get; set; }
          DateTime? AccessTokenIssueDateUtc { get; set; }
          DateTime? AccessTokenExpirationUtc { get; set; }
          HashSet<string> Scope { get; }
      } 

      AccessTokenExpirationUtc是AccessToken過(guò)期時(shí)間,以Utc時(shí)間為準(zhǔn)。若該對(duì)象為null,則表示尚未授權(quán),我們需要去授權(quán)服務(wù)端請(qǐng)求。

      private static AuthorizationServerDescription _authServerDescription = new AuthorizationServerDescription
      {
          TokenEndpoint = new Uri(MvcApplication.TokenEndpoint),
          AuthorizationEndpoint = new Uri(MvcApplication.AuthorizationEndpoint),
      };
      
      private static WebServerClient _client = new WebServerClient(_authServerDescription, "democlient", "samplesecret");
      
      [HttpPost]
      public ActionResult Index()
      {
          if (Authorization == null)
          {
              return _client.PrepareRequestUserAuthorization().AsActionResult();
          }
          return View();
      }

      AuthorizationServerDescription包含兩個(gè)屬性,AuthorizationEndpoint是用戶(hù)顯式授權(quán)的地址,一般即用戶(hù)輸用戶(hù)名密碼的地;TokenEndpoint是用授權(quán)碼換取AccessToken的地址,注意該地址須用POST請(qǐng)求?!癲emoclient”和“samplesecret”是示例用的客戶(hù)端ID和客戶(hù)端Secret。WebServerClient.PrepareRequestUserAuthorization方法將會(huì)首先返回code和state到當(dāng)前url,以querystring的形式(若用戶(hù)授權(quán)的話)。

      code即是授權(quán)碼,state參數(shù)不好理解,這涉及到CSRF,可參看淺談CSRF攻擊方式,state就是為了預(yù)防CSRF而引入的隨機(jī)數(shù)??蛻?hù)端生成該值,將其附加到state參數(shù)的同時(shí),存入用戶(hù)Cookie中,用戶(hù)授權(quán)完畢后,該參數(shù)會(huì)同授權(quán)碼一起返回到客戶(hù)端,然后客戶(hù)端將其值同Cookie中的值比較,若一樣則表示該次授權(quán)為當(dāng)前用戶(hù)操作,視為有效。由于不同域的cookie無(wú)法共享,因此其它站點(diǎn)并不能知道state的確切的值,CSRF攻擊也就無(wú)從談起了。簡(jiǎn)單地說(shuō),state參數(shù)起到一個(gè)標(biāo)示消息是否合法的作用。結(jié)合獲取授權(quán)碼這步來(lái)說(shuō),授權(quán)服務(wù)端返回的url為http://localhost:22187/?code=xxxxxxxxx&state=_PzGpfJzyQI9DkdoyWeWr格式,若忽略state,那么攻擊方將code替換成自己的授權(quán)碼,引誘用戶(hù)點(diǎn)擊,最終客戶(hù)端獲取的AccessToken是攻擊方的AccessToken,由于AccessToken同用戶(hù)關(guān)聯(lián),也就是說(shuō),后續(xù)客戶(hù)端做的其實(shí)是另一個(gè)用戶(hù)資源(也許是攻擊方注冊(cè)的虛擬用戶(hù)),如果操作中包括新增或更新,那么錄入的真實(shí)用戶(hù)信息就會(huì)被攻擊方獲取到。現(xiàn)在很多客戶(hù)端使用服務(wù)端的賬號(hào)進(jìn)行自身的登錄(類(lèi)似于OpenID),即賬號(hào)綁定,那么攻擊方即可用自己在服務(wù)端的賬號(hào)管理受害者在客戶(hù)端的賬號(hào)信息??蓞⒖?a class="question-hyperlink" rel="noopener nofollow">OAuth2 Cross Site Request Forgery, and state parameter、小議OAuth 2.0的state參數(shù)

      有了code就可以去換取AccessToken了:

      public ActionResult Index(string code,string state)
      {
          if (!string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(state))
          {
              var authorization = _client.ProcessUserAuthorization(Request);
              Authorization = authorization;
              return View(authorization);
          }
          return View();
      }

      如前所述,Authorization不為null即表示整個(gè)授權(quán)流程成功完成。然后就可以用它來(lái)請(qǐng)求資源了。

      public ActionResult Invoke()
      {
          var request = new HttpRequestMessage(new HttpMethod("GET"), "http://demo.openapi.cn/bookcates");
          using (var httpClient = new HttpClient(_client.CreateAuthorizingHandler(Authorization)))
          {
              using (var resourceResponse = httpClient.SendAsync(request))
              {
                  ViewBag.Result = resourceResponse.Result.Content.ReadAsStringAsync().Result;
              }
          }
          return View(Authorization);
      }

      WebServerClient.CreateAuthorizingHandler方法返回一個(gè)DelegatingHandler,主要用來(lái)當(dāng)AccessToken過(guò)期時(shí),使用RefreshToken刷新?lián)Q取新的AccessToken;并設(shè)置Authorization頭信息,下文有進(jìn)一步說(shuō)明。

      原生方式獲取AccessToken


      既然是開(kāi)放平臺(tái),面對(duì)的客戶(hù)端種類(lèi)自然多種多樣,DotNetOpenAuth.OAuth2.Client顯然就不夠用了,我也不打算為了這個(gè)學(xué)遍所有程序語(yǔ)言。所幸OAuth基于http,不管任何語(yǔ)言開(kāi)發(fā)的客戶(hù)端,獲取AccessToken的步驟本質(zhì)上就是提交http請(qǐng)求和接收http響應(yīng)的過(guò)程,客戶(hù)端SDK只是將這個(gè)過(guò)程封裝得更易用一些。下面就讓我們以授權(quán)碼模式為例,一窺究竟。

      參照前述事例,當(dāng)我們第一次(新的瀏覽器會(huì)話)在客戶(hù)端點(diǎn)擊“請(qǐng)求授權(quán)”按鈕后,會(huì)跳轉(zhuǎn)到授權(quán)服務(wù)端的授權(quán)界面。

      可以看到,url中帶了client_id、redirect_uri、state、response_type四個(gè)參數(shù),若要請(qǐng)求限定的授權(quán)范圍,還可以傳入scope參數(shù)。其中response_type設(shè)為code表示請(qǐng)求的是授權(quán)碼。

      以下為請(qǐng)求授權(quán)碼:

       1 private string GetNonCryptoRandomDataAsBase64(int binaryLength)
       2 {
       3     byte[] buffer = new byte[binaryLength];
       4     _random.NextBytes(buffer);
       5     string uniq = Convert.ToBase64String(buffer);
       6     return uniq;
       7 }
       8 
       9 public ActionResult DemoRequestCode()
      10 {
      11     string xsrfKey = this.GetNonCryptoRandomDataAsBase64(16);//生成隨機(jī)數(shù)
      12     string url = MvcApplication.AuthorizationEndpoint + "?" + 
      13         string.Format("client_id={0}&redirect_uri={1}&response_type={2}&state={3}",
      14         "democlient", "http://localhost:22187/", "code", xsrfKey);
      15     HttpCookie xsrfKeyCookie = new HttpCookie(XsrfCookieName, xsrfKey);
      16     xsrfKeyCookie.HttpOnly = true;
      17     xsrfKeyCookie.Secure = FormsAuthentication.RequireSSL;
      18     Response.Cookies.Add(xsrfKeyCookie);
      19 
      20     return Redirect(url);
      21 }

      授權(quán)碼返回后,先檢查state參數(shù),若通過(guò)則換取AccessToken:

      private bool VerifyState(string state)
      {
          var cookie = Request.Cookies[XsrfCookieName];
          if (cookie == null)
              return false;
      
          var xsrfCookieValue = cookie.Value;
          return xsrfCookieValue == state;
      }
      
      private AuthenticationHeaderValue SetAuthorizationHeader()
      {
          string concat = "democlient:samplesecret";
          byte[] bits = Encoding.UTF8.GetBytes(concat);
          string base64 = Convert.ToBase64String(bits);
          return new AuthenticationHeaderValue("Basic", base64);
      }
      
      public ActionResult Demo(string code, string state)
      {
          if (!string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(state) && VerifyState(state))
          {
              var httpClient = new HttpClient();
              var httpContent = new FormUrlEncodedContent(new Dictionary<string, string>()
          {
              {"code", code},
              {"redirect_uri", "http://localhost:22187/"},
              {"grant_type","authorization_code"}
          });
              httpClient.DefaultRequestHeaders.Authorization = this.SetAuthorizationHeader();
      
              var response = httpClient.PostAsync(MvcApplication.TokenEndpoint, httpContent).Result;
              Authorization = response.Content.ReadAsAsync<AuthorizationState>().Result;
              return View(Authorization);
          }
          return View();
      }

      如上所示,以Post方式提交,三個(gè)參數(shù),code即是授權(quán)碼,redirect_uri和獲取授權(quán)碼時(shí)傳遞的redirect_uri要保持一致,grant_type設(shè)置為“authorization_code”。注意SetAuthorizationHeader方法,需要設(shè)置請(qǐng)求頭的Authorization屬性,Scheme為“Basic”,Parameter為以Base64編碼的“客戶(hù)端ID:客戶(hù)端Secret”字符串。成功后返回的信息可以轉(zhuǎn)為前面說(shuō)的IAuthorizationState接口對(duì)象。 

      如前所述,當(dāng)AccessToken過(guò)期后,需要用RefreshToken刷新。

      private void RefreshAccessToken()
      {
          var httpClient = new HttpClient();
          var httpContent = new FormUrlEncodedContent(new Dictionary<string, string>()
          {
              {"refresh_token", Authorization.RefreshToken},
              {"grant_type","refresh_token"}
          });
          httpClient.DefaultRequestHeaders.Authorization = this.SetAuthorizationHeader();
      
          var response = httpClient.PostAsync(MvcApplication.TokenEndpoint, httpContent).Result;
          Authorization = response.Content.ReadAsAsync<AuthorizationState>().Result;
      }

      其中g(shù)rant_type須設(shè)置為”refresh_token”,請(qǐng)求頭信息設(shè)置同前。

      獲取AccessToken后,就可以用于訪問(wèn)用戶(hù)資源了。

      public ActionResult DemoInvoke()
      {
          var httpClient = new HttpClient();
          if (this.Authorization.AccessTokenExpirationUtc.HasValue && this.Authorization.AccessTokenExpirationUtc.Value < DateTime.UtcNow)
          {
              this.RefreshAccessToken();
          }
          var bearerToken = this.Authorization.AccessToken;
      
          httpClient = new HttpClient();
          httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
          var request = new HttpRequestMessage(new HttpMethod("GET"), "http://demo.openapi.cn/bookcates");
          using (var resourceResponse = httpClient.SendAsync(request))
          {
              ViewBag.Result = resourceResponse.Result.Content.ReadAsStringAsync().Result;
          }
          return View(Authorization);
      }

      用法很簡(jiǎn)單,Authorization請(qǐng)求頭,Scheme設(shè)為“Bearer”,Parameter為AccessToken即可。

       

      斷斷續(xù)續(xù)寫(xiě)了大半個(gè)月,到此終于可以舒一口氣了。需要完整代碼的朋友,我會(huì)過(guò)段時(shí)間補(bǔ)上。

      代碼鏈接在評(píng)論24#,有疑問(wèn)可參看我的后續(xù)隨筆:使用DotNetOpenAuth搭建OAuth2.0授權(quán)框架——Demo代碼簡(jiǎn)單說(shuō)明。

       

      其它參考資料:

      OAuth 1.0 簡(jiǎn)介

       

      轉(zhuǎn)載請(qǐng)注明本文出處:http://www.rzrgm.cn/newton/p/3409984.html

      posted @ 2013-11-26 20:56  萊布尼茨  閱讀(25647)  評(píng)論(56)    收藏  舉報(bào)
      主站蜘蛛池模板: 伊通| 看免费真人视频网站| 乱人伦中文字幕成人网站在线 | 久久国产精品波多野结衣| 亚洲精品777| 中文字幕av日韩有码| 99久久99久久久精品久久| 久久精品免视看国产成人| 国产又黄又硬又粗| 综合人妻久久一区二区精品| 欧美极品色午夜在线视频 | 九九九国产精品成人免费视频| 97人妻熟女成人免费视频色戒| 色综合久久精品亚洲国产| 廊坊市| 国产成人高清精品亚洲| 国产精品高清一区二区三区| 成午夜福利人试看120秒| 成人久久精品国产亚洲av| 精品熟女日韩中文十区| 国产亚洲999精品AA片在线爽| 免费人成自慰网站| 国产成人精品一区二区三| 亚洲精品中文av在线| 制服丝袜美腿一区二区| 国产精品午夜福利免费看| 波多野结衣一区二区免费视频| 国产精品自拍实拍在线看| 插入中文字幕在线一区二区三区| 人妻教师痴汉电车波多野结衣| 欧美不卡一区二区三区| 高清有码国产一区二区| 色色97| 亚洲综合中文字幕首页| 女的被弄到高潮娇喘喷水视频| 国产中文字幕在线一区| 亚洲性一交一乱一伦视频| 97欧美精品系列一区二区| 2021亚洲国产精品无码| 一本色道久久加勒比综合| 久久久久久久一线毛片|