在ASP.NET Web API 2中使用Owin基於Token令牌的身份驗證


基於令牌的身份驗證

基於令牌的身份驗證主要區別於以前常用的常用的基於cookie的身份驗證,基於cookie的身份驗證在B/S架構中使用比較多,但是在Web Api中因其特殊性,基於cookie的身份驗證已經不適合了,因為並不是每一個調用api的客戶端都是從瀏覽器發起,我們面臨的客戶端可能是手機、平板、或者app。

使用基於Token令牌的身份驗證有一些好處:

  • 服務器的可伸縮性:發送到服務器的令牌是字包含的,不依賴與共享會話存儲
  • 松散耦合:前端應用程序位於特定的身份驗證機制耦合,令牌是從服務器生成的,並且API構建方式可理解該令牌並進行身份驗證
  • 適用於移動設備:cookie依賴於瀏覽器,存儲於本機平台,使用token將簡化流程

構建后端API

步驟一: 創建Web Api 項目

 

為了進行代碼演示,創建一個相對比較干凈的環境,我們新建一個項目演示本次功能,本文使用Visual Studio 2017和 .NTE Framework 4.7。

在Vs中選擇新建項目,選擇ASP.NET Web 應用程序(.NET Framework) ,命名為OauthExample或者隨便你喜歡的名字,然后下一步,選擇空模板。ok

image

 

步驟二:安裝Owin包,並設置“StarUp”類

項目右鍵,管理Nuget程序包,分別安裝

Microsoft.AspNet.WebApi.Owin

Microsoft.Owin.Host.SystemWeb

也可以在程序包管理器輸入如下代碼安裝:

Install-Package Microsoft.AspNet.WebApi.Owin 
Install-Package Microsoft.Owin.Host.SystemWeb

等待安裝完成。

右鍵項目,移除Global.asax,右鍵項目,添加OWIN StartUp 類,然后修改代碼如下:

using System.Web.Http;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(OAuthExample.Startup))]

namespace OAuthExample
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 有關如何配置應用程序的詳細信息,請訪問 https://go.microsoft.com/fwlink/?LinkID=316888
            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
            app.UseWebApi(config);
        }
    }
}

簡要說明

  • assembly屬性設置了啟動時要觸發的類
  • HttpConfiguration對象用於配置API路由等,我們將對象傳遞給Register方法
  • UasWebApi接收對象config,該方法將把Web Api連接到我們的OWIN服務管道

完成后編譯一下,檢查是否能通過,如果有問題檢查一下Nuget包是否安裝正確。

 

步驟三:添加對OAuth承載令牌生成的支持

安裝Owin包,Microsoft.Owin.Security.OAuth,再次打開StartUp文件,修改代碼如下(斜體):

using System;
using System.Web.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;

[assembly: OwinStartup(typeof(OAuthExample.Startup))]

namespace OAuthExample
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 有關如何配置應用程序的詳細信息,請訪問 https://go.microsoft.com/fwlink/?LinkID=316888

            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/oauth/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = new CustomAuthorizationServerProvider() }; // Token Generation  app.UseOAuthAuthorizationServer(OAuthServerOptions); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
            app.UseWebApi(config);
        }
    }
}

在這里,我們從類“OAuthAuthorizationServerOptions”創建了新實例,並設置選項如下:

  • 允許客戶端使用http協議請求
  • 令牌生成路徑為"/oauth/token" ,即通過路徑host: port:/oauth/token 獲取令牌信息
  • 設置令牌的有效時間為一天,如果用戶在令牌發出24小時候使用令牌進行身份驗證,請求將被拒絕,並返回401狀態碼
  • 我們在名為“CustomAuthorizationServerProvider”的類中實現了如何用戶票據的驗證和發放

最后我們將此選項傳遞給擴展方法“ UseOAuthAuthorizationServer”,以便將身份驗證中間件添加到管道中。

 

步驟四:實現“CustomAuthorizationServerProvider”類

在項目中添加名為“ Providers”的新文件夾,然后添加名為“ SimpleAuthorizationServerProvider”的新類,在下面粘貼代碼片段:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin.Security.OAuth;

namespace OAuthExample.Providers
{
    public class CustomAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        /// <summary>
        /// Called to validate that the origin of the request is a registered "client_id", and that the correct credentials for that client are
        /// present on the request. If the web application accepts Basic authentication credentials,
        /// context.TryGetBasicCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request header. If the web
        /// application accepts "client_id" and "client_secret" as form encoded POST parameters,
        /// context.TryGetFormCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request body.
        /// If context.Validated is not called the request will not proceed further.
        /// </summary>
        /// <param name="context">The context of the event carries information in and results out.</param>
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
        }

        /// <summary>
        /// Called when a request to the Token endpoint arrives with a "grant_type" of "password". This occurs when the user has provided name and password
        /// credentials directly into the client application's user interface, and the client application is using those to acquire an "access_token" and
        /// optional "refresh_token". If the web application supports the
        /// resource owner credentials grant type it must validate the context.Username and context.Password as appropriate. To issue an
        /// access token the context.Validated must be called with a new ticket containing the claims about the resource owner which should be associated
        /// with the access token. The application should take appropriate measures to ensure that the endpoint isn’t abused by malicious callers.
        /// The default behavior is to reject this grant type.
        /// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
        /// </summary>
        /// <param name="context">The context of the event carries information in and results out.</param>
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {

            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

            //這里是驗證用戶名和密碼,可以根據項目情況自己實現
            if (!(context.UserName == "zhangsan" && context.Password == "123456"))
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }

            //可以隨便添加
            var identity = new ClaimsIdentity(context.Options.AuthenticationType);
            identity.AddClaim(new Claim("sub", context.UserName));
            identity.AddClaim(new Claim("role", "user"));

            context.Validated(identity);

        }
    }
}

步驟五:允許ASP.NET Web Api跨域請求

使用nuget安裝程序包,Install-Package Microsoft.Owin.Cors

然后在Startup類中添加如下代碼,最終代碼如下:

using System;
using System.Web.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using OAuthExample.Providers;
using Owin;

[assembly: OwinStartup(typeof(OAuthExample.Startup))]

namespace OAuthExample
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 有關如何配置應用程序的詳細信息,請訪問 https://go.microsoft.com/fwlink/?LinkID=316888

            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new CustomAuthorizationServerProvider()
            };

            // Token Generation
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }
    }
}

代碼測試

我們添加一個測試空的Order控制,用來測試一下上面的實現:

[RoutePrefix("api/Orders")]
    public class OrdersController : ApiController
    {
        [Authorize]
        [Route("")]
        public IHttpActionResult Get()
        {
            return Ok(Order.CreateOrders());
        }

    }

    #region Helpers

    public class Order
    {
        public int OrderID { get; set; }
        public string CustomerName { get; set; }
        public string ShipperCity { get; set; }
        public Boolean IsShipped { get; set; }

        public static List<Order> CreateOrders()
        {
            List<Order> OrderList = new List<Order> 
            {
                new Order {OrderID = 10248, CustomerName = "Taiseer Joudeh", ShipperCity = "Amman", IsShipped = true },
                new Order {OrderID = 10249, CustomerName = "Ahmad Hasan", ShipperCity = "Dubai", IsShipped = false},
                new Order {OrderID = 10250,CustomerName = "Tamer Yaser", ShipperCity = "Jeddah", IsShipped = false },
                new Order {OrderID = 10251,CustomerName = "Lina Majed", ShipperCity = "Abu Dhabi", IsShipped = false},
                new Order {OrderID = 10252,CustomerName = "Yasmeen Rami", ShipperCity = "Kuwait", IsShipped = true}
            };

            return OrderList;
        }
    }

    #endregion

 

下面使用PostMan進行模擬測試.

在未授權時,直接訪問 http://localhost:56638/api/orders得到如下結果:

image

模擬授權訪問,先獲取令牌:

image

 

將令牌附加到Order請求,再次嘗試訪問:

 

image

可以看到已經能正常獲取到數據,打開調試,看一下方法中的變量如下:

image

 

總結

一直覺得WebApi和MVC很多都一樣的東西,在實際應用中還是有不少區別,關於OAuth、JWT等等在WebApi中使用較多,本文是參照文末連接做的一個總結,細看下原po的時間都已經是14年的文章了。馬上要aspnet core 3.2都要發布了,現在卻還在補以前的知識,慚愧的很!

 

參考鏈接:

 Token Based Authentication using ASP.NET Web API 2, Owin, and Identity

Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM