自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Web API之認(rèn)證(Authentication)兩種實(shí)現(xiàn)方式

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

序言

對(duì)于所謂的認(rèn)證說到底就是安全問題,在Web API中有多種方式來(lái)實(shí)現(xiàn)安全,【accepted】方式來(lái)處理基于IIS的安全(通過上節(jié)提到的WindowsIdentity依賴于HttpContext和IIS認(rèn)證)或者在Web API里通過使用Web API中的消息處理機(jī)制,但是如果我們想應(yīng)用程序運(yùn)行在IIS之外此時(shí)Windows Idenitity這一方式似乎就不太可能了,同時(shí)在Web API中本身就未提供如何處理認(rèn)證的直接方式,我們不得不自定義來(lái)實(shí)現(xiàn)認(rèn)證功能,同時(shí)這也是我們所推薦的方式,自己動(dòng)手,豐衣足食。
溫馨提示:下面實(shí)現(xiàn)方法皆基于基礎(chǔ)認(rèn)證,若不熟悉Http協(xié)議中的Basic基礎(chǔ)認(rèn)證,請(qǐng)先參看此篇文章【園友海鳥-介紹Basic基礎(chǔ)認(rèn)證和Diges摘要認(rèn)證】。


無(wú)論何種方式,對(duì)于我們的應(yīng)用程序我們都需要在業(yè)務(wù)層使用基于憑證的用戶認(rèn)證,因?yàn)槭强蛻舳艘环降男枨?,所以客戶端需要明確基礎(chǔ)驗(yàn)證,基礎(chǔ)認(rèn)證(Basic)非常簡(jiǎn)單并且支持任何Web客戶端,但是基礎(chǔ)驗(yàn)證的缺點(diǎn)是不安全,通過使用SSL則可以進(jìn)行加密就可以在一定程度上保證了安全,如果是對(duì)于一般的應(yīng)用程序通過基礎(chǔ)認(rèn)證只是進(jìn)行編碼而未加密也可以說是安全的。我們還是看看上一節(jié)所給圖片

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

我們自定義一個(gè)認(rèn)證身份(用戶名和密碼)的類,那么此類必須也就要繼承于 GenericIdentity ,既然是基于基礎(chǔ)驗(yàn)證,那么類型當(dāng)然也就是Basic了。

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


第二步

我們要自定義一個(gè)認(rèn)證過濾器特性,并繼承 AuthorizationFilterAttribute ,此時(shí)會(huì)變成如下:

  1. public class BasicAuthenticationFilter : AuthorizationFilterAttribute 
  2. public override void OnAuthorization(HttpActionContext actionContext) 
  3. {} 

那么在這個(gè)重寫的方法我們應(yīng)該寫什么呢?我們慢慢來(lái)分析!請(qǐng)往下看。

解析請(qǐng)求報(bào)文頭

首先對(duì)于客戶單發(fā)送過來(lái)的請(qǐng)求我們肯定是需要獲得請(qǐng)求報(bào)頭,然后解析請(qǐng)求報(bào)頭中的Authorization,若此時(shí)其參數(shù)為空,我們將返回到客戶端,并發(fā)起質(zhì)詢。

 

  1.  string authParameter = null
  2.  
  3. var authValue = actionContext.Request.Headers.Authorization; //actionContext:Action方法請(qǐng)求上下文 
  4. if (authValue != null && authValue.Scheme == "Basic"
  5. authParameter = authValue.Parameter; //authparameter:獲取請(qǐng)求中經(jīng)過Base64編碼的(用戶:密碼) 
  6.  
  7. if (string.IsNullOrEmpty(authParameter)) 
  8.  
  9. return null
次之,若此時(shí)認(rèn)證中的參數(shù)不為空并開始對(duì)其進(jìn)行編碼,并返回一個(gè)BasicAuthenticationIdentity對(duì)象,若此時(shí)對(duì)象為空,則同樣返回到客戶端,并發(fā)起質(zhì)詢
  1. uthParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter)); //對(duì)編碼的參數(shù)進(jìn)行解碼 
  2.  
  3. var authToken = authParameter.Split(':'); //解碼后的參數(shù)格式為(用戶名:密碼)將其進(jìn)行分割 
  4. if (authToken.Length < 2
  5. return null
  6.  
  7. return new BasicAuthenticationIdentity(authToken[0], authToken[1]); //將分割的用戶名和密碼傳遞給此類構(gòu)造函數(shù)進(jìn)行初始化 


***,我們將上述兩者封裝為一個(gè)ParseHeader方法以便進(jìn)行調(diào)用

  1.  public virtual BasicAuthenticationIdentity ParseHeader(HttpActionContext actionContext) 
  2. string authParameter = null
  3.  
  4. var authValue = actionContext.Request.Headers.Authorization; 
  5. if (authValue != null && authValue.Scheme == "Basic"
  6. authParameter = authValue.Parameter; 
  7.  
  8. if (string.IsNullOrEmpty(authParameter)) 
  9.  
  10. return null
  11.  
  12. authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter)); 
  13.  
  14. var authToken = authParameter.Split(':'); 
  15. if (authToken.Length < 2
  16. return null
  17.  
  18. return new BasicAuthenticationIdentity(authToken[0], authToken[1]); 

接下來(lái)我們將認(rèn)證未通過而需要發(fā)起認(rèn)證質(zhì)詢,我們將其封裝為一個(gè)方法Challenge

  1. void Challenge(HttpActionContext actionContext) 
  2. var host = actionContext.Request.RequestUri.DnsSafeHost; 
  3. actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); 
  4. actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host)); 
  5.  

定義一個(gè)方法便于對(duì)用戶名和密碼進(jìn)行校驗(yàn),并將其修飾為虛方法,以免后續(xù)要添加其他有關(guān)用戶數(shù)

  1.  public virtual bool OnAuthorize(string userName, string userPassword, HttpActionContext actionContext) 
  2. if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(userPassword)) 
  3.  
  4. return false
  5. else 
  6. return true
  7.  

在認(rèn)證成功后將認(rèn)證身份設(shè)置給當(dāng)前線程中Principal屬性

  1. ar principal = new GenericPrincipal(identity, null); 
  2.  
  3. Thread.CurrentPrincipal = principal; 
  4.  
  5. //下面是針對(duì)ASP.NET而設(shè)置 
  6. //if (HttpContext.Current != null) 
  7. // HttpContext.Current.User = principal; 


第三步

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

  1.  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] 
  2. public class BasicAuthenticationFilter : AuthorizationFilterAttribute 
  3. public override void OnAuthorization(HttpActionContext actionContext) 
  4. var userIdentity = ParseHeader(actionContext); 
  5. if (userIdentity == null
  6. Challenge(actionContext); 
  7. return
  8.  
  9. if (!OnAuthorize(userIdentity.Name, userIdentity.Password, actionContext)) 
  10. Challenge(actionContext); 
  11. return
  12.  
  13. var principal = new GenericPrincipal(userIdentity, null); 
  14.  
  15. Thread.CurrentPrincipal = principal; 
  16.  
  17. base.OnAuthorization(actionContext); 

 #p#


第四步

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

  1.  public class CustomBasicAuthenticationFilter : BasicAuthenticationFilter 
  2. public override bool OnAuthorize(string userName, string userPassword, HttpActionContext actionContext) 
  3. if (userName == "xpy0928" && userPassword == "cnblogs"
  4.  
  5. return true
  6. else 
  7. return false
  8.  


***一步

注冊(cè)自定義認(rèn)證特性并進(jìn)行調(diào)用

  1.  config.Filters.Add(new CustomBasicAuthenticationFilter()); 
  2.  
  3. [CustomBasicAuthenticationFilter] 
  4. public class ProductController : ApiController 
  5. {....} 

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

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

我們點(diǎn)擊取消,觀察是否返回401并添加質(zhì)詢頭即WWW-Authenticate,如我們所料

我們輸入正確的用戶名和密碼再試試看,結(jié)果認(rèn)證成功,如下:

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

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

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

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

同樣是我們需要根據(jù)請(qǐng)求來(lái)解析請(qǐng)求報(bào)頭,我們依然需要解析報(bào)頭方法,但是需要稍作修改

 

  1.  public virtual BasicAuthenticationIdentity ParseHeader(HttpRequestMessage requestMessage) 
  2. string authParameter = null
  3.  
  4. var authValue = requestMessage.Headers.Authorization; 
  5. if (authValue != null && authValue.Scheme == "Basic"
  6. authParameter = authValue.Parameter; 
  7.  
  8. if (string.IsNullOrEmpty(authParameter)) 
  9.  
  10. return null
  11.  
  12. authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter)); 
  13.  
  14. var authToken = authParameter.Split(':'); 
  15. if (authToken.Length < 2
  16. return null
  17.  
  18. return new BasicAuthenticationIdentity(authToken[0], authToken[1]); 

此時(shí)質(zhì)詢也得作相應(yīng)的修改,因?yàn)榇藭r(shí)不再是依賴于Action請(qǐng)求上下文,而是請(qǐng)求(HttpRequestMessage)和響應(yīng)(HttpResponseMessage)

 

  1.  void Challenge(HttpRequestMessage request,HttpResponseMessage response) 
  2. var host = request.RequestUri.DnsSafeHost; 
  3.  
  4. response.Headers.Add(authenticationHeader, string.Format("Basic realm=\"{0}\"", host)); 
  5.  
  6. }

最終繼承自DelegatingHandler的代碼如

 

  1.  public class BasicAuthenticationHandler : DelegatingHandler 
  2. private const string authenticationHeader = "WWW-Authenticate"
  3. protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
  4. var crendentials = ParseHeader(request); 
  5.  
  6. if (crendentials != null
  7. var identity = new BasicAuthenticationIdentity(crendentials.Name, crendentials.Password); 
  8.  
  9. var principal = new GenericPrincipal(identity, null); 
  10.  
  11. Thread.CurrentPrincipal = principal; 
  12.  
  13. //針對(duì)于ASP.NET設(shè)置 
  14. //if (HttpContext.Current != null) 
  15. // HttpContext.Current.User = principal; 
  16.  
  17. return base.SendAsync(request, cancellationToken).ContinueWith(task => { 
  18. var response = task.Result; 
  19. if (crendentials == null && response.StatusCode == HttpStatusCode.Unauthorized) 
  20. Challenge(request, response); 
  21.  
  22. return response; 
  23. }); 
  24.  
  25.  
  26. void Challenge(HttpRequestMessage request,HttpResponseMessage response) 
  27. var host = request.RequestUri.DnsSafeHost; 
  28.  
  29. response.Headers.Add(authenticationHeader, string.Format("Basic realm=\"{0}\"", host)); 
  30.  
  31.  
  32. public virtual BasicAuthenticationIdentity ParseHeader(HttpRequestMessage requestMessage) 
  33. string authParameter = null
  34.  
  35. var authValue = requestMessage.Headers.Authorization; 
  36. if (authValue != null && authValue.Scheme == "Basic"
  37. authParameter = authValue.Parameter; 
  38.  
  39. if (string.IsNullOrEmpty(authParameter)) 
  40.  
  41. return null
  42.  
  43. authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter)); 
  44.  
  45. var authToken = authParameter.Split(':'); 
  46. if (authToken.Length < 2
  47. return null
  48.  
  49. return new BasicAuthenticationIdentity(authToken[0], authToken[1]); 

#p#
第三步

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

 

  1.  public class BasicAuthenticationFilter : AuthorizeAttribute 
  2. protected override bool IsAuthorized(HttpActionContext actionContext) 
  3.  
  4. var identity = Thread.CurrentPrincipal.Identity; 
  5. if (identity != null && HttpContext.Current != null
  6. identity = HttpContext.Current.User.Identity; 
  7.  
  8. if (identity != null && identity.IsAuthenticated) 
  9.  
  10. var basicAuthIdentity = identity as BasicAuthenticationIdentity; 
  11.  
  12. //可以添加其他需要的業(yè)務(wù)邏輯驗(yàn)證代碼 
  13. if (basicAuthIdentity.Name == "xpy0928" && basicAuthIdentity.Password == "cnblogs"
  14. return true
  15.  
  16. return false
  17.  

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

注冊(cè)自定義管道以及認(rèn)證過濾器特性

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

***一步

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

下面我們通過【360極速瀏覽器】來(lái)驗(yàn)收成果。點(diǎn)擊按鈕直接請(qǐng)求控制器

接下來(lái)取消,是否返回401

至此***結(jié)束。
總結(jié)
用認(rèn)證特性(AuthorizationFilterAttribute)還是HttpMessageHandler實(shí)現(xiàn)認(rèn)證,這是一個(gè)問題?
通過比較這二者的實(shí)現(xiàn)操作在實(shí)現(xiàn)方式上明顯有極大的不同,個(gè)人覺得用AuthorizationFilterAttribute來(lái)實(shí)現(xiàn)認(rèn)證是更加簡(jiǎn)單并且緊湊,因?yàn)閷?shí)現(xiàn)的每一處都在每一個(gè)地方,在大多數(shù)實(shí)現(xiàn)自定義登陸的場(chǎng)景下,對(duì)于用過濾器如此緊湊的業(yè)務(wù)邏輯用這個(gè)更加高效, 用HttpMessageHandler的優(yōu)點(diǎn)是全局應(yīng)用且是Web API消息處理管道的一部分,如果對(duì)于不同的部分要用不同的認(rèn)證那么用HttpMessageHandler效果更好,但是此時(shí)你需要自定義一個(gè)過濾器,尤其是當(dāng)MessageHandler對(duì)于一個(gè)認(rèn)證需要一個(gè)過濾器的時(shí)候。所以綜上所述,根據(jù)不同的應(yīng)用場(chǎng)景我們應(yīng)該選擇對(duì)應(yīng)的方式來(lái)實(shí)現(xiàn)認(rèn)證。

責(zé)任編輯:chenqingxiang 來(lái)源: xpy0928的博客
相關(guān)推薦

2023-03-29 13:06:36

2024-04-28 18:28:12

API文檔生成工具開發(fā)Web API

2021-12-08 10:47:35

RabbitMQ 實(shí)現(xiàn)延遲

2022-06-08 15:12:34

前端前端截圖

2010-07-14 10:30:26

Perl多線程

2011-03-03 10:26:04

Pureftpd

2009-04-03 09:00:20

SQL Server2005用戶

2009-06-15 15:02:48

Spring定時(shí)器

2010-09-28 15:12:27

Javascript

2010-09-07 11:09:59

2024-01-09 09:09:45

RESTGraphQL

2010-07-13 14:54:15

Perl面向?qū)ο缶幊?/a>

2023-05-31 19:10:31

2020-05-11 13:03:03

SR-TEIP路由器

2024-12-19 00:12:02

APIJSON數(shù)據(jù)

2010-08-06 09:38:11

Flex讀取XML

2010-04-20 15:32:20

主控負(fù)載均衡

2009-06-23 18:18:13

SpringHibernate

2021-10-19 10:56:00

插件工程方式

2009-06-25 13:43:00

Buffalo AJA
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)