一、Forms認證
1、在webapi項目中啟用Forms認證
Why:為什么要在WebAPI中使用Forms認證?因為其它項目使用的是Forms認證。
What:什么是Forms認證?它在WebAPI中就是一個MessageHandle,具體請查找關鍵字“ASP.NET Forms”
How:如何啟動Forms認證?
最簡單的是通過配置啟動Forms認證:
1 <system.web> 2 <authentication mode="Forms"> 3 <forms name=".FormsCookie" loginUrl="/login.aspx" protection="All" timeout="43200" path="/" defaultUrl="http://www.cnblogs.com" domain=".cnblogs.com" cookieless="UseCookies" /> 4 </authentication> 5 <httpCookies httpOnlyCookies="true" /> 6 </system.web> 7 <system.webServer>
簡單說說Forms認證的工作原理:首先在管道中,Forms讀取請求中的相關的cookie,解密,進行認證,並把認證的結果寫到請求上下文和線程的Identity屬性中。然后請求繼續往后面走,最終生成的響應在管道中返回時,Forms會判斷如果響應為401,那么就location到配置中的loginUrl設置的地址,並改變status為302。
2、幾個Attribute
Why:為什么要認識Attribute?因為Forms認證的結果是寫到Identity屬性中,一般我們要獲取該屬性,判斷是否認證成功,如果失敗返回401,等等進行很多處理。是不是很麻煩?對,封裝起來,自己寫一個嗎?當然可以,其實微軟大法早就考慮好了,針對一般的場景的處理邏輯都封裝好了,他們分別叫做
AuthorizeAttribute、AllowAnonymousAttribute,都是Attribute。
What:這些Attribute是什么?顧名思義,AuthorizeAttribute只允許認證通過的請求,AllowAnonymousAttribute允許匿名請求。
How:那么該怎么用呢?很簡單他們可以作用類型、方法上,所以可以全局注冊、controller、action, so easy!
3、重寫unauthorize中驗證失敗方法
Why:因為如果response status == 401,那么Forms會location到配置中的loginUrl,(即使沒有手動配置它,也會生成一個默認值“login.aspx”),並且設置status為302。如果客戶端是瀏覽器的話,那么就會直接進行跳轉而無法捕獲這個狀態,這在很多場景下不合適,例如:spa(單頁應用)中,我們不希望它自動跳轉到登陸頁面,而是給出提示,讓用戶自己選擇是否登錄。所以要重寫Forms中身份驗證失敗的處理邏輯。
How:在AuthorizationFilterAttribute中有個虛方法HandleUnauthorizedRequest,重寫它來實現自定義的處理邏輯。這樣的設計思路挺不錯,可以多借鑒。
/// <summary> /// If unauthorize return 403 instead of 401, avoid redirect. /// </summary> public class ForbiddenLocationAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) { HttpResponseMessage response = new HttpResponseMessage(); response.StatusCode = System.Net.HttpStatusCode.Forbidden; actionContext.Response = response; } }
使用403(Forbidden)來代替401,這樣就可以避免Forms的自動跳轉了。雖然這樣做有些弊端,但這也不失為一個有效的解決辦法。
二、ModelValidata(模型驗證)
1、Why
凡是有用戶輸入的地方都少不了參數驗證,這不光是個安全問題,也是為了保證數據完整正確。
2、What
WebAPI集成了模型驗證機制,當請求被action執行之前,有一個模型綁定的步驟,它就是匹配action的參數,具體細節就不說了,ModelValidata就是在這里進行,它會根據Model中各個屬性的DataAnnotations(數據注解)來進行驗證,最終把結果保存在action的上下文的一個屬性中,即actionContext.ModelState.IsValid。
3、How
a、為Model設置DataAnnotations
public class BannerDto { [JsonProperty(PropertyName = "id")] [Required(ErrorMessage = "Id是必須的")] public Guid Id { get; set; } [JsonProperty(PropertyName = "title")] [Required(ErrorMessage = "標題不能為空")] [MaxLength(200, ErrorMessage = "標題不能超過200個字符")] public string Title { get; set; } [JsonProperty(PropertyName = "src")] [Required(ErrorMessage = "圖片鏈接不能為空")] [MaxLength(500, ErrorMessage = "圖片鏈接不能超過500個字符")] public string ImageUri { get; set; } [JsonProperty(PropertyName = "href")] [Required(ErrorMessage = "超鏈接不能為空")] [MaxLength(500, ErrorMessage = "超鏈接不能超過500個字符")] public string Href { get; set; } [JsonIgnore] public Guid? AuthorityId { get; set; } [JsonIgnore] public bool IsDeleted { get; set; } = false; [JsonProperty(PropertyName ="createDate")] public DateTime CreateDate { get; set; } }
ps:[JsonProperty]、[JsonIgnore]是指定Json序列化的一些相關設置,設置別名、忽略等等。返回優雅的變量的名稱,保證代碼風格。
DataAnnotations的使用,請查看msdn,太簡單了。設置好約束條件和ErrorMessage,當驗證失敗了,就會返回ErrorMessage。
b、使用Filter方式,為Action添加驗證,好處就不多說了。
public class ValidataModelAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { if (!actionContext.ModelState.IsValid) { actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState); } } }
別忘了在HttpComfiguration中注入這個Filter
//模型驗證 config.Filters.Add(new ValidataModelAttribute());
對於不希望不驗證的Action可以使用OverrideActionFilters重寫上級設置的所有Fiters。
ModelValidate失敗的請求會得到400的響應,同時所有ErrorMessage都會在響應報文中,例如:
{"Message":"The request is invalid.","ModelState":{"sub.Href":["超鏈接不可為空"]}}
