程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> OAuth學習之基於DotNetOpenAuth實現Client Credentials Grant

OAuth學習之基於DotNetOpenAuth實現Client Credentials Grant

編輯:關於.NET

Client Credentials Grant是指直接由Client向Authorization Server請求access token,無需用戶(Resource Owner)的授權。比如我們提供OpenAPI讓大家可以獲取園子首頁最新隨筆,只需驗證一下Client是否有權限調用該API,不需要用戶的授權。而如果Client需要進行發布博客的操作,就需要用戶的授權,這時就要采用Authorization Code Grant。

DotNetOpenAuth是當前做得做好的基於.NET的OAuth開源實現,項目網址:https://github.com/DotNetOpenAuth。

Client Credentials Grant的流程圖如下(圖片1來源,圖片2來源):

一、Client向Authorization Server請求access token

主要操作如下:

1. 由client_id和client_secret構建出credentials。

2. 將credentials以http basic authentication的方式發送給Authorization Server。

3. 從Authorization Server的響應中提取access token

Client的實現代碼如下:

public async Task<ActionResult> SiteHome()
{
    var client_id = "m.cnblogs.com";
    var client_secret = "20140213";
    var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(client_id + ":" + client_secret));
    
    var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
    var httpContent = new FormUrlEncodedContent(new
    Dictionary<string, string>
    {
        {"grant_type", "client_credentials"}
    });
    
    var response = await httpClient.PostAsync("https://authserver.open.cnblogs.com/oauth/token", httpContent);
    
    var responseContent = await response.Content.ReadAsStringAsync();
    if (response.StatusCode == System.Net.HttpStatusCode.OK)
    {
        var accessToken = JObject.Parse(responseContent)["access_token"].ToString();
        return Content("AccessToken: " + accessToken);              
    }
    else
    {
        return Content(responseContent);
    }
}

二、Authorization Server驗證Client,發放access token

主要操作如下:

1. Authorization Server通過IAuthorizationServerHost.GetClient()獲取當前Client。

2. Authorization Server通過IClientDescription.IsValidClientSecret()驗證當前Client。

3. 驗證通過後,將access token包含在響應中發送給Client。

主要實現代碼如下(基於ASP.NET MVC):

1. Authorization Server中Client實體類的實現代碼(關鍵代碼是IsValidClientSecret()的實現):

public class Client : IClientDescription
    {
        public string Id { get; set; }
    
        public string Secret { get; set; }
    
        public Uri DefaultCallback
        {
            get { throw new NotImplementedException(); }
        }
    
        private ClientType _clientType;
        public ClientType ClientType
        {
            get { return _clientType; }
            set { _clientType = value; }
        }
    
        public bool HasNonEmptySecret
        {
            get { throw new NotImplementedException(); }
        }
    
        public bool IsCallbackAllowed(Uri callback)
        {
            throw new NotImplementedException();
        }
    
        public bool IsValidClientSecret(string secret)
        {
            return this.Secret == secret;
        }
    }

AuthorizationServerHost的代碼(關鍵代碼是GetClient()與CreateAccessToken()的實現):

public class AuthorizationServerHost : IAuthorizationServerHost
{
    public static readonly ICryptoKeyStore HardCodedCryptoKeyStore = new HardCodedKeyCryptoKeyStore("...");
    
    public IClientDescription GetClient(string clientIdentifier)
    {
        return ServiceLocator.GetService<IClientService>().GetClient(clientIdentifier);
    }
    
    public AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage)
    {
        var accessToken = new AuthorizationServerAccessToken
        {
            Lifetime = TimeSpan.FromHours(10),
            SymmetricKeyStore = this.CryptoKeyStore,
        };
        var result = new AccessTokenResult(accessToken);
        return result;
    }
    
    public AutomatedAuthorizationCheckResponse CheckAuthorizeClientCredentialsGrant(IAccessTokenRequest accessRequest)
    {
        //...
    }
    
    public AutomatedUserAuthorizationCheckResponse CheckAuthorizeResourceOwnerCredentialGrant
        (string userName, string password, IAccessTokenRequest accessRequest)
    {
        //...
    }        
    
    public DotNetOpenAuth.Messaging.Bindings.ICryptoKeyStore CryptoKeyStore
    {
        get { return HardCodedCryptoKeyStore; }
    }
    
    public bool IsAuthorizationValid(IAuthorizationDescription authorization)
    {
        return true;
    }
    
    public INonceStore NonceStore
    {
        get { return null; }
    }
}

查看本欄目

三、Client通過access token調用Resource Server上的API

主要實現代碼如下:

public async Task<ActionResult> HomePosts(string blogApp)
{
    //獲取access token的代碼見第1部分
    //...
    var accessToken = JObject.Parse(responseContent)["access_token"].ToString();
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    response = await httpClient.GetAsync("https://api.open.cnblogs.com/blog/posts/sitehome");
    return Content(await response.Content.ReadAsStringAsync());               
}

四、Resource Server驗證Client的access token,響應Client的API調用請求

主要實現代碼如下(基於ASP.NET Web API):

1. 通過MessageHandler統一驗證access token

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MessageHandlers.Add(new BearerTokenHandler());
    }
}

2. BearerTokenHandler的實現代碼(來自DotNetOpenAuth的示例代碼):

public class BearerTokenHandler : DelegatingHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        if (request.Headers.Authorization != null && request.Headers.Authorization.Scheme == "Bearer")
        {
            var resourceServer = new DotNetOpenAuth.OAuth2.ResourceServer
                (new StandardAccessTokenAnalyzer
                (AuthorizationServerHost.HardCodedCryptoKeyStore));
    
                var principal = await resourceServer.GetPrincipalAsync(request, cancellationToken);
                HttpContext.Current.User = principal;
                Thread.CurrentPrincipal = principal;
        }
    
        return await base.SendAsync(request, cancellationToken);
    }
}

3. Web API的示例實現代碼:

public class PostsController : ApiController
{
    [Route("blog/posts/sitehome")]
    public async Task<IEnumerable<string>> GetSiteHome()
    {
        return new string[] { User.Identity.Name };
    }
}

四、Client得到Resouce Server的響應結果

根據上面的Resouce Server中Web API的示例實現代碼,得到的結果是:

["client:m.cnblogs.com"]

小結

看起來比較簡單,但實際摸索的過程是曲折的。分享出來,也許可以讓初次使用DotNetOpenAuth的朋友少走一些彎路。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved