http://www.cnblogs.com/dudu/p/4569857.htmlhtml
OAuth真是一个复杂的东东,即便你把OAuth规范滚瓜烂熟,在具体实现时也会无从下手。所以,Microsoft.Owin.Security.OAuth应运而生(它的实现代码在Katana项目中),帮助开发者偷了很多工,减了很多料。api
这篇博文试图经过一个简单的示例分享一下如何基于Microsoft.Owin.Security.OAuth,使用Client Credentials Grant受权方式给客户端发放access token。app
Client Credentials Grant的受权方式就是只验证客户端(Client),不验证用户(Resource Owner),只要客户端经过验证就发access token。举一个对应的应用场景例子,好比咱们想提供一个“获取网站首页最新博文列表”的WebAPI给iOS App调用。因为这个数据与用户无关,因此不涉及用户登陆与受权,不须要Resource Owner的参与。但咱们不想任何人均可以调用这个WebAPI,因此要对客户端进行验证,而使用OAuth中的 Client Credentials Grant 受权方式能够很好地解决这个问题。ide
具体实现方式以下:测试
1)用Visual Studio 2013/2015建立一个Web API项目,VS会生成一堆OAuth相关代码。网站
2)打开Startup.Auth.cs ,精简一下代码,咱们只须要实现以Client Credentials Grant受权方式拿到token,其它无关代码所有清除,最终剩下以下代码:ui
public partial class Startup { public void ConfigureAuth(IAppBuilder app) { var OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/token"), Provider = new CNBlogsAuthorizationServerProvider(), AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), AllowInsecureHttp = true }; app.UseOAuthBearerTokens(OAuthOptions); } }
3)建立一个新的类 CNBlogsAuthorizationServerProvider,并继承自 OAuthAuthorizationServerProvider,重载 OAuthAuthorizationServerProvider() 与 GrantClientCredentials() 这两个方法。代码以下:spa
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 进行验证。code
在 GrantClientCredentials() 方法中对客户端进行受权,授了权就能发 access token 。orm
这样,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); } }