前言:采用Client Credentials方式,即密鑰key/password,場景一般是分為客戶端限制必須有權限才能使用的模塊,這和微信公眾號開放平台很類似。

讓用戶通過客戶端去獲取自己的token,在根據這個token去獲取資源。
本地登錄憑據流
- 用戶輸入名稱和密碼到客戶端。
- 客戶端將這些憑據發送到授權服務器。
- 授權服務器驗證憑據並返回訪問令牌。
- 要訪問受保護資源,客戶端在HTTP請求的Authorization標頭中包含訪問令牌。
服務實現:
使用WebApi基於Microsoft.Owin.Security.OAuth實現,新建一個空為WebApi項目。
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
namespace ApiThrottleDemo
{
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
/// <summary>
/// 驗證客戶[client_id與client_secret驗證]
/// </summary>
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//http://localhost:48339/token
string client_id;
string client_secret;
context.TryGetFormCredentials(out client_id, out client_secret);
if (client_id == "zara" && client_secret == "123456")
{
context.Validated(client_id);
}
else
{
//context.Response.StatusCode = Convert.ToInt32(HttpStatusCode.OK);
context.SetError("invalid_client", "client is not valid");
}
return base.ValidateClientAuthentication(context);
}
/// <summary>
/// 客戶端授權[生成access token]
/// </summary>
public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, "iphone"));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties() { AllowRefresh = true });
context.Validated(ticket);
return base.GrantClientCredentials(context);
}
/// <summary>
/// 刷新Token[刷新refresh_token]
/// </summary>
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
//enforce client binding of refresh token
if (context.Ticket == null || context.Ticket.Identity == null || !context.Ticket.Identity.IsAuthenticated)
{
context.SetError("invalid_grant", "Refresh token is not valid");
}
return base.GrantRefreshToken(context);
}
}
}
在此其中呢,需要繼承OAuthAuthorizationServerProvider,並重寫自己想重寫的方法,其內部定義下圖所示:

當然這還沒完,我們還需要去配置應用程序。在Startup.cs,我們要開啟BearerToken認證模式;該Provider屬性指定了一個插入OWIN中間件的提供程序,並處理由中間件引發的事件。
以下是應用想要獲取令牌時的基本流程:
要獲取訪問令牌,應用程序會向〜/ Token發送請求。
OAuth中間件調用GrantResourceOwnerCredentials提供程序。
提供程序調用ApplicationUserManager以驗證憑據並創建聲明標識。
如果成功,則提供程序會創建一個身份驗證票證,用於生成令牌。
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new ApplicationOAuthProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
AuthenticationMode = AuthenticationMode.Active,
AllowInsecureHttp = true
});
其中AccessTokenExpireTimeSpan參數是token過期時間,AllowInsecureHttp 是否開啟安全驗證,TokenEndpointPath就是你獲取token對於服務器的相對路徑,那我們都知道用戶只能訪問我們的Api,那如何在api上去走Oauth呢?
客戶端獲取票據
在控制器種創建一個控制器,命名為:OAuth2Controller。
[RoutePrefix("api/v1/oauth2")]
public class OAuth2Controller : ApiController
{
[Authorize]
[Route("news")]
public async Task<IHttpActionResult> GetNewsAsync()
{
var authentication = HttpContext.Current.GetOwinContext().Authentication;
var ticket = authentication.AuthenticateAsync("Bearer").Result;
var claimsIdentity = User.Identity as ClaimsIdentity;
var data = claimsIdentity.Claims.Where(c => c.Type == "urn:oauth:scope").ToList();
var claims = ((ClaimsIdentity)Thread.CurrentPrincipal.Identity).Claims;
return Ok(new { IsError = true, Msg = string.Empty, Data = Thread.CurrentPrincipal.Identity.Name + " It's about news !!! token expires: " + ticket.Properties.Dictionary.ToString() });
}
}
啟用授權驗證[WebApiConfig]
在ASP.NET Web API中啟用Token驗證,需要加上[Authorize]標記,並且配置默認啟用驗證不記名授權方式
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
服務端[/token]獲取token需要三個參數,我們使用大家熟悉的PostMan去試一試吧,啟動項目。

那我們不難看到,已經成功獲取了access_token,至於這個token的值,只要你的client_id不同它就一定是不會相同的(實在不行你可以搞個GUID),那么我們再構建一個ajax去模擬的獲取token吧。
做個簡單的頁面:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<input type="text" placeholder="client_id"/><br />
<input type="text" placeholder="client_secret"/><br />
<input type="text" placeholder="your_token"/><br />
<button>獲取token</button>
</body>
<script>
$(function () {
$("button").click(function () {
$.ajax({
url: "http://localhost:58560/token",
type: "post",
data: "grant_type=client_credentials&client_id=zara&client_secret=123456",
success: function (res) {
console.log(res);
}
})
})
})
</script>
</html>
這是生成的token。

最后總結,你可以在ValidateClientAuthentication中進行身份判斷,如果有這個身份,那么我就存儲DB中,這樣的話,類似於一個微信身份授權的功能基本上就是這樣了。
