日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

用Middleware给ASP.NET Core Web API添加自己的授权验证

發布時間:2023/12/4 asp.net 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用Middleware给ASP.NET Core Web API添加自己的授权验证 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Web API,是一個能讓前后端分離、解放前后端生產力的好東西。不過大部分公司應該都沒能做到完全的前后端分離。API的實現方式有很

多,可以用ASP.NET Core、也可以用ASP.NET Web API、ASP.NET MVC、NancyFx等。說到Web API,不同的人有不同的做法,可能前臺、

中臺和后臺各一個api站點,也有可能一個模塊一個api站點,也有可能各個系統共用一個api站點,當然這和業務有必然的聯系。

  安全順其自然的成為Web API關注的重點之一。現在流行的OAuth 2.0是個很不錯的東西,不過本文是暫時沒有涉及到的,只是按照最最最

原始的思路做的一個授權驗證。在之前的MVC中,我們可能是通過過濾器來處理這個身份的驗證,在Core中,我自然就是選擇Middleware來處

理這個驗證。

  下面開始本文的正題:

  先編寫一個能正常運行的api,不進行任何的權限過濾。

using Dapper;

using Microsoft.AspNetCore.Mvc;

using System.Data;

using System.Linq;

using System.Threading.Tasks;

using WebApi.CommandText;

using WebApi.Common;

using Common;


namespace WebApi.Controllers

{

? ? [Route("api/[controller]")]

? ? public class BookController : Controller

? ? {


? ? ? ? private DapperHelper _helper;

? ? ? ? public BookController(DapperHelper helper)

? ? ? ? {

? ? ? ? ? ? this._helper = helper;

? ? ? ? }


? ? ? ? // GET: api/book

? ? ? ? [HttpGet]

? ? ? ? public async Task<IActionResult> Get()

? ? ? ? {

? ? ? ? ? ? var res = await _helper.QueryAsync(BookCommandText.GetBooks);

? ? ? ? ? ? CommonResult<Book> json = new CommonResult<Book>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Code = "000",

? ? ? ? ? ? ? ? Message = "ok",

? ? ? ? ? ? ? ? Data = res

? ? ? ? ? ? };

? ? ? ? ? ? return Ok(json);

? ? ? ? }


? ? ? ? // GET api/book/5

? ? ? ? [HttpGet("{id}")]

? ? ? ? public IActionResult Get(int id)

? ? ? ? {

? ? ? ? ? ? DynamicParameters dp = new DynamicParameters();

? ? ? ? ? ? dp.Add("@Id", id, DbType.Int32, ParameterDirection.Input);

? ? ? ? ? ? var res = _helper.Query<Book>(BookCommandText.GetBookById, dp, null, true, null, CommandType.StoredProcedure).FirstOrDefault();

? ? ? ? ? ? CommonResult<Book> json = new CommonResult<Book>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Code = "000",

? ? ? ? ? ? ? ? Message = "ok",

? ? ? ? ? ? ? ? Data = res

? ? ? ? ? ? };

? ? ? ? ? ? return Ok(json);

? ? ? ? }


? ? ? ? // POST api/book ? ? ? ?

? ? ? ? [HttpPost]

? ? ? ? public IActionResult Post([FromForm]PostForm form)

? ? ? ? {

? ? ? ? ? ? DynamicParameters dp = new DynamicParameters();

? ? ? ? ? ? dp.Add("@Id", form.Id, DbType.Int32, ParameterDirection.Input);

? ? ? ? ? ? var res = _helper.Query<Book>(BookCommandText.GetBookById, dp, null, true, null, CommandType.StoredProcedure).FirstOrDefault();

? ? ? ? ? ? CommonResult<Book> json = new CommonResult<Book>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Code = "000",

? ? ? ? ? ? ? ? Message = "ok",

? ? ? ? ? ? ? ? Data = res

? ? ? ? ? ? };

? ? ? ? ? ? return Ok(json);

? ? ? ? }


? ? }


? ? public class PostForm

? ? {

? ? ? ? public string Id { get; set; }

? ? }


}

  api這邊應該沒什么好說的,都是一些常規的操作,會MVC的應該都可以懂。主要是根據id獲取圖書信息的方法(GET和POST)。這是我們后

面進行單元測試的兩個主要方法。這樣部署得到的一個API站點,是任何一個人都可以訪問http://yourapidomain.com/api/book 來得到相關

的數據。現在我們要對這個api進行一定的處理,讓只有權限的站點才能訪問它。

  下面就是編寫自定義的授權驗證中間件了。

  Middleware這個東西大家應該都不會陌生了,OWIN出來的時候就有中間件這樣的概念了,這里就不展開說明,在ASP.NET Core中是如何

實現這個中間件的可以參考官方文檔?Middleware。?

  我們先定義一個我們要用到的option,ApiAuthorizedOptions

namespace WebApi.Middlewares

{

? ? public class ApiAuthorizedOptions

? ? {

? ? ? ? //public string Name { get; set; }


? ? ? ? public string EncryptKey { get; set; }

? ? ? ??

? ? ? ? public int ExpiredSecond { get; set; }

? ? }

}

 option內容比較簡單,一個是EncryptKey ,用于對我們的請求參數進行簽名,另一個是ExpiredSecond ,用于檢驗我們的請求是否超時。

與之對應的是在appsettings.json中設置的ApiKey節點

"ApiKey": {

? ? //"username": "123",

? ? //"password": "123",

? ? "EncryptKey": "@*api#%^@",

? ? "ExpiredSecond": "300"

? }

有了option,下面就可以編寫middleware的內容了

  我們的api中就實現了get和post的方法,所以這里也就對get和post做了處理,其他http method,有需要的可以自己補充。

  這里的驗證主要是下面的幾個方面:

  1.參數是否被篡改

  2.請求是否已經過期

  3.請求的應用是否合法

  主檢查方法:Check



 Check方法帶了2個參數,一個是當前的httpcontext對象和請求的內容信息,當簽名一致,并且時間戳能轉化成double時才去校驗是否超時

和Applicatioin的相關信息。這里的簽名用了比較簡單的HMACMD5加密,同樣是可以換成SHA等加密來進行這一步的處理,加密的參數和規則是

隨便定的,要有一個約定的過程,缺少靈活性(就像跟銀行對接那樣,銀行說你就要這樣傳參數給我,不這樣就不行,只好乖乖從命)。

  Check方法還用到了下面的4個處理

  1.子檢查方法--超時判斷CheckExpiredTime


 這里取了當前時間與1970年1月1日的間隔與請求參數中傳過來的時間戳進行比較,是否超過我們在appsettings中設置的那個值,超過就是

超時了,沒超過就可以繼續下一個步驟。

  2.子檢查方法--應用程序判斷CheckApplication

  應用程序要驗證什么呢?我們會給每個應用程序創建一個ID和一個訪問api的密碼,所以我們要驗證這個應用程序的真實身份,是否是那些有權限的應用程序。

?

先根據請求參數中的應用程序id去找到相應的應用程序,不能找到就說明不是合法的應用程序,能找到再去驗證其密碼是否正確,最后才確

定其能否取得api中的數據。

  下面兩方法是處理沒有授權和超時處理的實現:

  沒有授權的返回方法ReturnNoAuthorized


  這里做的處理是將響應的狀態碼設置成401(Unauthorized)。

  超時的返回方法ReturnTimeOut


這里做的處理是將響應的狀態碼設置成408(Time Out)。

  下面就要處理Http的GET請求和POST請求了。

  HTTP GET請求的處理方法GetInvoke


  處理比較簡單,將請求的參數賦值給RequestInfo,然后將當前的httpcontext和這個requestinfo交由我們的主檢查方法Check去校驗

這個請求的合法性。

  同理,HTTP POST請求的處理方法PostInvoke,也是同樣的處理。


最后是Middleware的構造函數和Invoke方法。


 到這里,Middleware是已經編寫好了,要在Startup中使用,還要添加一個拓展方法ApiAuthorizedExtensions

using Microsoft.AspNetCore.Builder;

using Microsoft.Extensions.Options;

using System;


  到這里我們已經可以在Startup的Configure和ConfigureServices方法中配置這個中間件了

  這里還有一個不一定非要實現的拓展方法ApiAuthorizedServicesExtensions,但我個人還是傾向于實現這個ServicesExtensions。

using Microsoft.Extensions.DependencyInjection;

using System;


namespace WebApi.Middlewares

{

? ? public static class ApiAuthorizedServicesExtensions

? ? {


? ? ? ? /// <summary>

? ? ? ? /// Add response compression services.

? ? ? ? /// </summary>

? ? ? ? /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>

? ? ? ? /// <returns></returns>

? ? ? ? public static IServiceCollection AddApiAuthorized(this IServiceCollection services)

? ? ? ? {

? ? ? ? ? ? if (services == null)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? throw new ArgumentNullException(nameof(services));

? ? ? ? ? ? }


? ? ? ? ? ? return services;

? ? ? ? }


? ? ? ? /// <summary>

? ? ? ? /// Add response compression services and configure the related options.

? ? ? ? /// </summary>

? ? ? ? /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>

? ? ? ? /// <param name="configureOptions">A delegate to configure the <see cref="ResponseCompressionOptions"/>.</param>

? ? ? ? /// <returns></returns>

? ? ? ? public static IServiceCollection AddApiAuthorized(this IServiceCollection services, Action<ApiAuthorizedOptions> configureOptions)

? ? ? ? {

? ? ? ? ? ? if (services == null)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? throw new ArgumentNullException(nameof(services));

? ? ? ? ? ? }

? ? ? ? ? ? if (configureOptions == null)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? throw new ArgumentNullException(nameof(configureOptions));

? ? ? ? ? ? }


? ? ? ? ? ? services.Configure(configureOptions);

? ? ? ? ? ? return services;

? ? ? ? }

? ? }

}


ApiAuthorizedServicesExtensions

 為什么要實現這個拓展方法呢?個人認為

  Options、Middleware、Extensions、ServicesExtensions這四個是實現一個中間件的標配(除去簡單到不行的那些中間件)

  Options給我們的中間件提供了一些可選的處理,提高了中間件的靈活性;

  Middleware是我們中間件最最重要的實現;

  Extensions是我們要在Startup的Configure去表明我們要使用這個中間件;

  ServicesExtensions是我們要在Startup的ConfigureServices去表明我們把這個中間件添加到容器中。

  下面是完整的Startup

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Logging;

using System;

using WebApi.Common;

using WebApi.Middlewares;


namespace WebApi

{

? ? public class Startup

? ? {

? ? ? ? public Startup(IHostingEnvironment env)

? ? ? ? {

? ? ? ? ? ? var builder = new ConfigurationBuilder()

? ? ? ? ? ? ? ? .SetBasePath(env.ContentRootPath)

? ? ? ? ? ? ? ? .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)

? ? ? ? ? ? ? ? .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);


? ? ? ? ? ? if (env.IsEnvironment("Development"))

? ? ? ? ? ? {

? ? ? ? ? ? ? ? // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.

? ? ? ? ? ? ? ? builder.AddApplicationInsightsSettings(developerMode: true);

? ? ? ? ? ? }


? ? ? ? ? ? builder.AddEnvironmentVariables();

? ? ? ? ? ? Configuration = builder.Build();

? ? ? ? }


? ? ? ? public IConfigurationRoot Configuration { get; }


? ? ? ? // This method gets called by the runtime. Use this method to add services to the container

? ? ? ? public void ConfigureServices(IServiceCollection services)

? ? ? ? {

? ? ? ? ? ? // Add framework services.

? ? ? ? ? ? services.AddApplicationInsightsTelemetry(Configuration);

? ? ? ? ? ? services.Configure<IISOptions>(options =>

? ? ? ? ? ? {


? ? ? ? ? ? });


? ? ? ? ? ? services.Configure<DapperOptions>(options =>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? options.ConnectionString = Configuration.GetConnectionString("DapperConnection");

? ? ? ? ? ? });


? ? ? ? ? ? //api authorized middleware

? ? ? ? ? ? services.AddApiAuthorized(options =>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? options.EncryptKey = Configuration.GetSection("ApiKey")["EncryptKey"];

? ? ? ? ? ? ? ? options.ExpiredSecond = Convert.ToInt32(Configuration.GetSection("ApiKey")["ExpiredSecond"]);

? ? ? ? ? ? });



? ? ? ? ? ? services.AddMvc();


? ? ? ? ? ? services.AddSingleton<DapperHelper>();

? ? ? ? }


? ? ? ? // This method gets called by the runtime. Use this method to configure the HTTP request pipeline

? ? ? ? public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

? ? ? ? {


? ? ? ? ? ? loggerFactory.AddConsole(Configuration.GetSection("Logging"));

? ? ? ? ? ? loggerFactory.AddDebug();


? ? ? ? ? ? app.UseDapper();


? ? ? ? ? ? //api authorized middleware

? ? ? ? ? ? app.UseApiAuthorized();


? ? ? ? ? ? app.UseApplicationInsightsRequestTelemetry();


? ? ? ? ? ? app.UseApplicationInsightsExceptionTelemetry();


? ? ? ? ? ? app.UseMvc();

? ? ? ? }

? ? }

}

萬事具備,只欠測試!!

  建個類庫項目,寫個單元測試看看。

using Common;

using Newtonsoft.Json;

using System;

using System.Collections.Generic;

using System.Net.Http;

using System.Threading.Tasks;

using Xunit;


namespace WebApiTest

{

? ? public class BookApiTest

? ? {

? ? ? ? private HttpClient _client;

? ? ? ? private string applicationId = "1";

? ? ? ? private string applicationPassword = "123";

? ? ? ? private string timestamp = (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds.ToString();

? ? ? ? private string nonce = new Random().Next(1000, 9999).ToString();

? ? ? ? private string signature = string.Empty;


? ? ? ? public BookApiTest()

? ? ? ? {

? ? ? ? ? ? _client = new HttpClient();

? ? ? ? ? ? _client.BaseAddress = new Uri("http://localhost:8091/");

? ? ? ? ? ? _client.DefaultRequestHeaders.Clear();

? ? ? ? ? ? signature = HMACMD5Helper.GetEncryptResult($"{applicationId}-{timestamp}-{nonce}", "@*api#%^@");

? ? ? ? }


? ? ? ? [Fact]

? ? ? ? public async Task book_api_get_by_id_should_success()

? ? ? ? {

? ? ? ? ? ? string queryString = $"applicationId={applicationId}&timestamp={timestamp}&nonce={nonce}&signature={signature}&applicationPassword={applicationPassword}";

? ? ? ? ? ??

? ? ? ? ? ? HttpResponseMessage message = await _client.GetAsync($"api/book/4939?{queryString}");

? ? ? ? ? ? var result = JsonConvert.DeserializeObject<CommonResult<Book>>(message.Content.ReadAsStringAsync().Result);


? ? ? ? ? ? Assert.Equal("000", result.Code);

? ? ? ? ? ? Assert.Equal(4939, result.Data.Id);

? ? ? ? ? ? Assert.True(message.IsSuccessStatusCode);

? ? ? ? }


? ? ? ? [Fact]

? ? ? ? public async Task book_api_get_by_id_should_failure()

? ? ? ? {

? ? ? ? ? ? string inValidSignature = Guid.NewGuid().ToString();

? ? ? ? ? ? string queryString = $"applicationId={applicationId}&timestamp={timestamp}&nonce={nonce}&signature={inValidSignature}&applicationPassword={applicationPassword}";


? ? ? ? ? ? HttpResponseMessage message = await _client.GetAsync($"api/book/4939?{queryString}");

? ? ? ? ? ? var result = JsonConvert.DeserializeObject<CommonResult<Book>>(message.Content.ReadAsStringAsync().Result);


? ? ? ? ? ? Assert.Equal("401", result.Code);

? ? ? ? ? ? Assert.Equal(System.Net.HttpStatusCode.Unauthorized, message.StatusCode); ? ? ? ? ? ?

? ? ? ? }


? ? ? ? [Fact]

? ? ? ? public async Task book_api_post_by_id_should_success()

? ? ? ? { ? ? ? ? ? ? ?

? ? ? ? ? ? var data = new Dictionary<string, string>();

? ? ? ? ? ? data.Add("applicationId", applicationId);

? ? ? ? ? ? data.Add("applicationPassword", applicationPassword);

? ? ? ? ? ? data.Add("timestamp", timestamp);

? ? ? ? ? ? data.Add("nonce", nonce);

? ? ? ? ? ? data.Add("signature", signature);

? ? ? ? ? ? data.Add("Id", "4939");

? ? ? ? ? ? HttpContent ct = new FormUrlEncodedContent(data);


? ? ? ? ? ? HttpResponseMessage message = await _client.PostAsync("api/book", ct);

? ? ? ? ? ? var result = JsonConvert.DeserializeObject<CommonResult<Book>>(message.Content.ReadAsStringAsync().Result);


? ? ? ? ? ? Assert.Equal("000", result.Code);

? ? ? ? ? ? Assert.Equal(4939, result.Data.Id);

? ? ? ? ? ? Assert.True(message.IsSuccessStatusCode);


? ? ? ? }


? ? ? ? [Fact]

? ? ? ? public async Task book_api_post_by_id_should_failure()

? ? ? ? {

? ? ? ? ? ? string inValidSignature = Guid.NewGuid().ToString();

? ? ? ? ? ? var data = new Dictionary<string, string>();

? ? ? ? ? ? data.Add("applicationId", applicationId);

? ? ? ? ? ? data.Add("applicationPassword", applicationPassword);

? ? ? ? ? ? data.Add("timestamp", timestamp);

? ? ? ? ? ? data.Add("nonce", nonce);

? ? ? ? ? ? data.Add("signature", inValidSignature);

? ? ? ? ? ? data.Add("Id", "4939");

? ? ? ? ? ? HttpContent ct = new FormUrlEncodedContent(data);


? ? ? ? ? ? HttpResponseMessage message = await _client.PostAsync("api/book", ct);

? ? ? ? ? ? var result = JsonConvert.DeserializeObject<CommonResult<Book>>(message.Content.ReadAsStringAsync().Result);


? ? ? ? ? ? Assert.Equal("401", result.Code);

? ? ? ? ? ? Assert.Equal(System.Net.HttpStatusCode.Unauthorized, message.StatusCode);

? ? ? ? }

? ? } ??

}

 測試用的是XUnit。這里寫了get和post的測試用例。

  下面來看看測試的效果。

?

?  測試通過。這里是直接用VS自帶的測試窗口來運行測試,比較直觀。

  當然也可以通過我們的dotnet test命令來運行測試。

  本文的Demo已經上傳到Github:

  https://github.com/hwqdt/Demos/tree/master/src/ASPNETCoreAPIAuthorizedDemo

原文地址:http://www.cnblogs.com/catcher1994/p/6021046.html


.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

總結

以上是生活随笔為你收集整理的用Middleware给ASP.NET Core Web API添加自己的授权验证的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。