【乾貨】基於Owin WebApi 使用OAuth2進行客戶端受權服務

前言:採用Client Credentials方式,即密鑰key/password,場景通常是分爲客戶端限制必須有權限才能使用的模塊,這和微信公衆號開放平臺很相似。html

讓用戶經過客戶端去獲取本身的token,在根據這個token去獲取資源。jquery

本地登陸憑據流

  1. 用戶輸入名稱和密碼到客戶端。
  2. 客戶端將這些憑據發送到受權服務器。
  3. 受權服務器驗證憑據並返回訪問令牌。
  4. 要訪問受保護資源,客戶端在HTTP請求的Authorization標頭中包含訪問令牌。

服務實現:

使用WebApi基於Microsoft.Owin.Security.OAuth實現,新建一個空爲WebApi項目。ajax

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,並重寫本身想重寫的方法,其內部定義下圖所示:api

 固然這還沒完,咱們還須要去配置應用程序。在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呢?app

客戶端獲取票據

在控制器種建立一個控制器,命名爲:OAuth2Controller。iphone

 [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]async

在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中,這樣的話,相似於一個微信身份受權的功能基本上就是這樣了。

相關文章
相關標籤/搜索