在ASP.NET中基於Owin OAuth使用Client Credentials Grant授權發放Token

OAuth真是一個複雜的東東,即使你把OAuth規範倒背如流,在具體實現時也會無從下手。因此,Microsoft.Owin.Security.OAuth應運而生(它的實現代碼在Katana項目中),幫助開發者偷了不少工,減了不少料。

這篇博文試圖通過一個簡單的示例分享一下如何基於Microsoft.Owin.Security.OAuth,使用Client Credentials Grant授權方式給客戶端發放access token。

Client Credentials Grant的授權方式就是隻驗證客戶端(Client),不驗證用戶(Resource Owner),只要客戶端通過驗證就發access token。舉一個對應的應用場景例子,比如我們想提供一個“獲取網站首頁最新博文列表”的WebAPI給iOS App調用。由於這個數據與用戶無關,所以不涉及用戶登錄與授權,不需要Resource Owner的參與。但我們不想任何人都可以調用這個WebAPI,所以要對客戶端進行驗證,而使用OAuth中的 Client Credentials Grant (客戶端憑據授予)授權方式可以很好地解決這個問題。

具體實現方式如下:

1)用Visual Studio 2013/2015創建一個Web API項目,VS會生成一堆OAuth相關代碼。

2)打開Startup.Auth.cs ,精簡一下代碼,我們只需要實現以Client Credentials Grant授權方式拿到token,其它無關代碼全部清除,最終剩下如下代碼:

複製代碼

public partial class Startup
{    public void ConfigureAuth(IAppBuilder app)
    {        var OAuthOptions = new OAuthAuthorizationServerOptions
        {
        //TokenEndpointPath是生成Token的終結點,這個不需要我們寫額外的邏輯了,
        //因爲Token的生成涉及到很多方面,例如序列化反序列加密解密等邏輯,所以框架默認
        //已經幫我們做好了
            TokenEndpointPath = new PathString("/token"),
            Provider = new CNBlogsAuthorizationServerProvider(),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

        app.UseOAuthBearerTokens(OAuthOptions);
    }
}

複製代碼

3)創建一個新的類 CNBlogsAuthorizationServerProvider,並繼承自 OAuthAuthorizationServerProvider,重載 OAuthAuthorizationServerProvider() 與 GrantClientCredentials() 這兩個方法。代碼如下:

複製代碼

public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{   
     public override Task                 ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {       
         string clientId;   
         string clientSecret;
        context.TryGetFormCredentials(out clientId, out clientSecret);        if (clientId == "1234" && clientSecret == "5678")
        {
            context.Validated(clientId);
        }        return base.ValidateClientAuthentication(context);
    }    public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
    {        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, "iOS App"));        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);        return base.GrantClientCredentials(context);
    }
}

複製代碼

在 ValidateClientAuthentication() 方法中獲取客戶端的 client_id 與 client_secret 進行驗證。

在 GrantClientCredentials() 方法中對客戶端進行授權,授了權就能發 access token 。

這樣,OAuth的服務端代碼就完成了。這麼簡單?是的,就這麼簡單,因爲有了Microsoft.Owin.Security.OAuth。

4)然後寫客戶端調用代碼測試一下:

複製代碼

public class OAuthClientTest
{    private HttpClient _httpClient;    public OAuthClientTest()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
    }

    [Fact]    public void Get_Accesss_Token_By_Client_Credentials_Grant()
    {        var parameters = new Dictionary<string, string>();
        parameters.Add("client_id", "1234");
        parameters.Add("client_secret", "5678");
        parameters.Add("grant_type", "client_credentials");

        Console.WriteLine(_httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters))
            .Result.Content.ReadAsStringAsync().Result);
    }
}

複製代碼

運行結果如下:

{"access_token":"8PqaWilv_SJT7vRXambP7Mebyaf3KO1GXYHsqA-oPMOQF6xk1YpluczOZGo-WwATU5YmGb0wSR0cUQMC8RSZfwO8nwom7yG11FIANhy2PNiqTg2CYdJF0sf0ggFs6it_i3mc_m1iEFCK2dLBPDJXPI24wngCPR0wP_zugZvyKv314BM0PQmnnwg3kLXR1DISKRbs5-i59VCtFSZgkM7A0w","token_type":"bearer","expires_in":1209599}

搞定!

【更新】 

建議使用Basic Authentication傳遞clientId與clientSecret,服務端CNBlogsAuthorizationServerProvider中的TryGetFormCredentials()改爲TryGetBasicCredentials(),客戶端的調用代碼如下:

複製代碼

public class OAuthClientTest
{    private HttpClient _httpClient;    public OAuthClientTest()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
    }

    [Fact]    public void Get_Accesss_Token_By_Client_Credentials_Grant()
    {        var clientId =  "1234";        var clientSecret = "5678";        
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(                "Basic",
                Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret)));                
        var parameters = new Dictionary<string, string>();        
        parameters.Add("grant_type", "client_credentials");       

        Console.WriteLine(_httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters))
            .Result.Content.ReadAsStringAsync().Result);
    }
}

複製代碼


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章