基於令牌的身份驗證主要區別於之前經常使用的經常使用的基於cookie的身份驗證,基於cookie的身份驗證在B/S架構中使用比較多,可是在Web Api中因其特殊性,基於cookie的身份驗證已經不適合了,由於並非每個調用api的客戶端都是從瀏覽器發起,咱們面臨的客戶端多是手機、平板、或者app。html
使用基於Token令牌的身份驗證有一些好處:前端
爲了進行代碼演示,建立一個相對比較乾淨的環境,咱們新建一個項目演示本次功能,本文使用Visual Studio 2017和 .NTE Framework 4.7。angularjs
在Vs中選擇新建項目,選擇ASP.NET Web 應用程序(.NET Framework) ,命名爲OauthExample或者隨便你喜歡的名字,而後下一步,選擇空模板。okweb
項目右鍵,管理Nuget程序包,分別安裝後端
Microsoft.AspNet.WebApi.Owinapi
Microsoft.Owin.Host.SystemWeb跨域
也能夠在程序包管理器輸入以下代碼安裝:瀏覽器
Install-Package Microsoft.AspNet.WebApi.Owin
Install-Package Microsoft.Owin.Host.SystemWeb
等待安裝完成。服務器
右鍵項目,移除Global.asax,右鍵項目,添加OWIN StartUp 類,而後修改代碼以下:cookie
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); } } }
簡要說明
完成後編譯一下,檢查是否能經過,若是有問題檢查一下Nuget包是否安裝正確。
安裝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」建立了新實例,並設置選項以下:
最後咱們將此選項傳遞給擴展方法「 UseOAuthAuthorizationServer」,以便將身份驗證中間件添加到管道中。
在項目中添加名爲「 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); } } }
使用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獲得以下結果:
模擬受權訪問,先獲取令牌:
將令牌附加到Order請求,再次嘗試訪問:
能夠看到已經能正常獲取到數據,打開調試,看一下方法中的變量以下:
一直以爲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