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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Web APi之认证(Authentication)两种实现方式【二】(十三)

發(fā)布時間:2025/4/16 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Web APi之认证(Authentication)两种实现方式【二】(十三) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

上一節(jié)我們詳細講解了認證及其基本信息,這一節(jié)我們通過兩種不同方式來實現(xiàn)認證,并且分析如何合理的利用這兩種方式,文中涉及到的基礎知識,請參看上一篇文中,就不再敘述廢話。

序言

對于所謂的認證說到底就是安全問題,在Web API中有多種方式來實現(xiàn)安全,【accepted】方式來處理基于IIS的安全(通過上節(jié)提到的WindowsIdentity依賴于HttpContext和IIS認證)或者在Web API里通過使用Web API中的消息處理機制,但是如果我們想應用程序運行在IIS之外此時Windows Idenitity這一方式似乎就不太可能了,同時在Web API中本身就未提供如何處理認證的直接方式,我們不得不自定義來實現(xiàn)認證功能,同時這也是我們所推薦的方式,自己動手,豐衣足食。

溫馨提示:下面實現(xiàn)方法皆基于基礎認證,若不熟悉Http協(xié)議中的Basic基礎認證,請先參看此篇文章【園友海鳥-介紹Basic基礎認證和Digest摘要認證

?

無論何種方式,對于我們的應用程序我們都需要在業(yè)務層使用基于憑證的用戶認證,因為是客戶端一方的需求,所以客戶端需要明確基礎驗證,基礎認證(Basic)非常簡單并且支持任何Web客戶端,但是基礎驗證的缺點是不安全,通過使用SSL則可以進行加密就可以在一定程度上保證了安全,如果是對于一般的應用程序通過基礎認證只是進行編碼而未加密也可以說是安全的。我們還是看看上一節(jié)所給圖片

通過上述圖片的粗略信息我們可以看出在請求到Action方法之間要經(jīng)過Web API消息處理管道,在請求到目標元素之前要經(jīng)過HttpMessageHandler和認證過濾器,所以我們可以通過這兩者來自定義實現(xiàn)認證。下面我們一一來看。

基于Web API的認證過濾器(AuthorizationFilterAttribute)實現(xiàn)認證

第一步

我們自定義一個認證身份(用戶名和密碼)的類,那么此類必須也就要繼承于?GenericIdentity?,既然是基于基礎驗證,那么類型當然也就是Basic了。

public class BasicAuthenticationIdentity : GenericIdentity{public string Password { get; set; }public BasicAuthenticationIdentity(string name, string password): base(name, "Basic"){this.Password = password;}}

第二步

我們要自定義一個認證過濾器特性,并繼承?AuthorizationFilterAttribute?,此時會變成如下:

public class BasicAuthenticationFilter : AuthorizationFilterAttribute{public override void OnAuthorization(HttpActionContext actionContext){}}

那么在這個重寫的方法我們應該寫什么呢?我們慢慢來分析!請往下看。

  • 解析請求報文頭

首先對于客戶端發(fā)送過來的請求我們肯定是需要獲得請求報頭,然后解析請求報頭中的Authorization,若此時其參數(shù)為空,我們將返回到客戶端,并發(fā)起質詢。

string authParameter = null;var authValue = actionContext.Request.Headers.Authorization; //actionContext:Action方法請求上下文if (authValue != null && authValue.Scheme == "Basic")authParameter = authValue.Parameter; //authparameter:獲取請求中經(jīng)過Base64編碼的(用戶:密碼)if (string.IsNullOrEmpty(authParameter))return null;

次之,若此時認證中的參數(shù)不為空并開始對其進行解碼,并返回一個BasicAuthenticationIdentity對象,若此時對象為空,則同樣返回到客戶端,并發(fā)起質詢

authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter)); //對編碼的參數(shù)進行解碼var authToken = authParameter.Split(':'); //解碼后的參數(shù)格式為(用戶名:密碼)將其進行分割if (authToken.Length < 2)return null;return new BasicAuthenticationIdentity(authToken[0], authToken[1]); //將分割的用戶名和密碼傳遞給此類構造函數(shù)進行初始化

最后,我們將上述兩者封裝為一個ParseHeader方法以便進行調用?

public virtual BasicAuthenticationIdentity ParseHeader(HttpActionContext actionContext){string authParameter = null;var authValue = actionContext.Request.Headers.Authorization;if (authValue != null && authValue.Scheme == "Basic")authParameter = authValue.Parameter;if (string.IsNullOrEmpty(authParameter))return null;authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter));var authToken = authParameter.Split(':');if (authToken.Length < 2)return null;return new BasicAuthenticationIdentity(authToken[0], authToken[1]);}
  • 接下來我們將認證未通過而需要發(fā)起認證質詢,我們將其封裝為一個方法Challenge

void Challenge(HttpActionContext actionContext){var host = actionContext.Request.RequestUri.DnsSafeHost;actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));}?
  • 定義一個方法便于對用戶名和密碼進行校驗,并將其修飾為虛方法,以免后續(xù)要添加其他有關用戶數(shù)據(jù)

public virtual bool OnAuthorize(string userName, string userPassword, HttpActionContext actionContext){if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(userPassword))return false;elsereturn true;}
  • ?在認證成功后將認證身份設置給當前線程中Principal屬性

var principal = new GenericPrincipal(identity, null);Thread.CurrentPrincipal = principal;//下面是針對ASP.NET而設置//if (HttpContext.Current != null)// HttpContext.Current.User = principal;

第三步

一切已經(jīng)就緒,此時在重寫方法中進行相應的調用即可,如下:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]public class BasicAuthenticationFilter : AuthorizationFilterAttribute{public override void OnAuthorization(HttpActionContext actionContext){var userIdentity = ParseHeader(actionContext);if (userIdentity == null){Challenge(actionContext);return;}if (!OnAuthorize(userIdentity.Name, userIdentity.Password, actionContext)){Challenge(actionContext);return;}var principal = new GenericPrincipal(userIdentity, null);Thread.CurrentPrincipal = principal;base.OnAuthorization(actionContext);}

第四步?

自定義?CustomBasicAuthenticationFilter?并繼承于?BasicAuthenticationFilter?,重寫其虛方法。

public class CustomBasicAuthenticationFilter : BasicAuthenticationFilter{public override bool OnAuthorize(string userName, string userPassword, HttpActionContext actionContext){if (userName == "xpy0928" && userPassword == "cnblogs")return true;elsereturn false;}}

最后一步

注冊自定義認證特性并進行調用

config.Filters.Add(new CustomBasicAuthenticationFilter());[CustomBasicAuthenticationFilter]public class ProductController : ApiController{....}

至此對于其認證方式就已經(jīng)完全實現(xiàn),接下來我們通過【搜狗瀏覽器】來驗收我們的成果。

看到如下認證其用戶名和密碼的圖片,我們知道我們成功了一半

我們點擊取消,觀察是否返回401并添加質詢頭即WWW-Authenticate,如我們所料

我們輸入正確的用戶名和密碼再試試看,結果認證成功,如下:

基于Web API的消息處理管道(HttpMessageHandler)實現(xiàn)認證

我們知道HttpMessageHandler是Web API中請求-響應中的消息處理管道的重要角色,但是真正實現(xiàn)管道串聯(lián)的是DelegatingHandler,若你不懂Web API消息管道,請參考前面系列文章,所以我們可以自定義管道來進行攔截通過繼承DelegatingHandler。下面我們一步步來實現(xiàn)基于此管道的認證。

第一步

和第一種方法一致不再敘述。

第二步

這一步當然是自定義管道進行處理并繼承DelegatingHandler,重載在此類中的SendAsync方法,通過獲得其請求并處理從而進行響應,若不懂此類中的具體實現(xiàn),請參看前面系列文章。

  • 同樣是我們需要根據(jù)請求來解析請求報頭,我們依然需要解析報頭方法,但是需要稍作修改

public virtual BasicAuthenticationIdentity ParseHeader(HttpRequestMessage requestMessage){string authParameter = null;var authValue = requestMessage.Headers.Authorization;if (authValue != null && authValue.Scheme == "Basic")authParameter = authValue.Parameter;if (string.IsNullOrEmpty(authParameter))return null;authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter));var authToken = authParameter.Split(':');if (authToken.Length < 2)return null;return new BasicAuthenticationIdentity(authToken[0], authToken[1]);}
  • 此時質詢也得作相應的修改,因為此時不再是依賴于Action請求上下文,而是請求(HttpRequestMessage)和響應(HttpResponseMessage)

void Challenge(HttpRequestMessage request,HttpResponseMessage response){var host = request.RequestUri.DnsSafeHost;response.Headers.Add(authenticationHeader, string.Format("Basic realm=\"{0}\"", host));}
  • 最終繼承自DelegatingHandler的代碼如下

public class BasicAuthenticationHandler : DelegatingHandler{private const string authenticationHeader = "WWW-Authenticate";protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){var crendentials = ParseHeader(request);if (crendentials != null){var identity = new BasicAuthenticationIdentity(crendentials.Name, crendentials.Password);var principal = new GenericPrincipal(identity, null);Thread.CurrentPrincipal = principal;//針對于ASP.NET設置//if (HttpContext.Current != null)// HttpContext.Current.User = principal; }return base.SendAsync(request, cancellationToken).ContinueWith(task => {var response = task.Result;if (crendentials == null && response.StatusCode == HttpStatusCode.Unauthorized){Challenge(request, response);}return response;});}void Challenge(HttpRequestMessage request,HttpResponseMessage response){var host = request.RequestUri.DnsSafeHost;response.Headers.Add(authenticationHeader, string.Format("Basic realm=\"{0}\"", host));}public virtual BasicAuthenticationIdentity ParseHeader(HttpRequestMessage requestMessage){string authParameter = null;var authValue = requestMessage.Headers.Authorization;if (authValue != null && authValue.Scheme == "Basic")authParameter = authValue.Parameter;if (string.IsNullOrEmpty(authParameter))return null;authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter));var authToken = authParameter.Split(':');if (authToken.Length < 2)return null;return new BasicAuthenticationIdentity(authToken[0], authToken[1]);}}

第三步?

上述我們自定義的BasicAuthenticationFilter此時就得繼承?AuthorizeAttribute?該特性也是繼承于上述的?AuthorizationFilterAttribute?,我們需要利用AuthorizeAttribute中的?IsAuthorized?方法來驗證當前線程中的Principal是否已經(jīng)被授權。

public class BasicAuthenticationFilter : AuthorizeAttribute{protected override bool IsAuthorized(HttpActionContext actionContext){var identity = Thread.CurrentPrincipal.Identity;if (identity != null && HttpContext.Current != null)identity = HttpContext.Current.User.Identity;if (identity != null && identity.IsAuthenticated){var basicAuthIdentity = identity as BasicAuthenticationIdentity;
//可以添加其他需要的業(yè)務邏輯驗證代碼
if (basicAuthIdentity.Name == "xpy0928" && basicAuthIdentity.Password == "cnblogs"){return true;}}return false;}}

通過?IsAuthorized?方法返回值來看,若為false,則返回401狀態(tài)碼,此時會觸發(fā)?BasicAuthenticationHandler ?中的質詢,并且此方法里面主要是我們需要添加認證用戶的業(yè)務邏輯代碼。同時我們也說過我們第一種方法自定義實現(xiàn)的過濾器特性是?AuthorizationFilterAttribute?(如果我們有更多邏輯使用這個特性是個不錯的選擇),而在這里是?AuthorizeAttribute?(對于驗證用戶并且返回bool值使用此過濾器特性是個不錯的選擇)。

第四步

注冊自定義管道以及認證過濾器特性

config.MessageHandlers.Add(new BasicAuthenticationHandler());config.Filters.Add(new BasicAuthenticationFilter());

最后一步

[BasicAuthenticationFilter]public class ProductController : ApiController{.....}

下面我們通過【360極速瀏覽器】來驗收成果。點擊按鈕直接請求控制器

接下來取消,是否返回401

至此完美結束。

總結?

用認證特性(AuthorizationFilterAttribute)還是HttpMessageHandler實現(xiàn)認證,這是一個問題??

通過比較這二者的實現(xiàn)操作在實現(xiàn)方式上明顯有極大的不同,個人覺得用AuthorizationFilterAttribute來實現(xiàn)認證是更加簡單并且緊湊,因為實現(xiàn)的每一處都在每一個地方,在大多數(shù)實現(xiàn)自定義登陸的場景下,對于用過濾器如此緊湊的業(yè)務邏輯用這個更加高效, 用HttpMessageHandler的優(yōu)點是全局應用且是Web API消息處理管道的一部分,如果對于不同的部分要用不同的認證那么用HttpMessageHandler效果更好,但是此時你需要自定義一個過濾器,尤其是當MessageHandler對于一個認證需要一個過濾器的時候。所以綜上所述,根據(jù)不同的應用場景我們應該選擇對應的方式來實現(xiàn)認證。

?

源代碼鏈接

 WebAPi之認證.7z

轉載于:https://www.cnblogs.com/CreateMyself/p/4857799.html

總結

以上是生活随笔為你收集整理的Web APi之认证(Authentication)两种实现方式【二】(十三)的全部內容,希望文章能夠幫你解決所遇到的問題。

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