.net Forms Authentication - 手動設置 HttpContext.Current.User 在自定義 AuthorizeAttribute 中不起作用
我已經打了這個幾個小時了,我很難過。我正在向 MVC 5 控制器發出 ajax 發布請求,以嘗試自動登錄特定的預定義“超級”使用者。在控制器方法中,我試圖以程式方式設置 HttpContext.Current.User 並進行身份驗證,因此超級使用者可以跳過手動登錄的過程。對此的共識似乎在這裡,我實現了:
在我嘗試使用自定義 AuthorizeAttribute 查看任何其他控制器方法之前,這似乎有效。
控制器方法:
[HttpPost] [AllowAnonymous] public ActionResult Login(string username) { string password = ConfigurationManager.AppSettings["Pass"]; User user = service.Login(username, password); var name = FormsAuthentication.FormsCookieName; var cookie = Response.Cookies[name]; if (cookie != null) { var ticket = FormsAuthentication.Decrypt(cookie.Value); if (ticket != null && !ticket.Expired) { string[] roles = (ticket.UserData as string ?? "").Split(','); System.Web.HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), roles); } } //...processing result return Json(result); }上面的 service.Login 方法創建了 cookie:
FormsAuthentication.SetAuthCookie(cookieValue, false);雖然我正在設置具有身份且 IsAuthenticated 為真的使用者,但下面的 filterContext.HttpContext.User 不是同一個使用者。它本質上是空的,就好像它從未被分配過,也沒有經過身份驗證。
public override void OnAuthorization(AuthorizationContext filterContext) { string[] userDetails = filterContext.HttpContext.User.Identity.Name.Split(char.Parse("|")); }我能找到的最接近的文章在這裡:IsAuthenticated 適用於瀏覽器 - 但不適用於 Air 客戶端!
但是,我已經為我解決了這個問題:
<authentication mode="Forms"> <forms cookieless="UseCookies" timeout="60" loginUrl="~/Account/Login" /> </authentication>為了使 AuthorizationContext.User 與我在控制器中進行身份驗證的 HttpContext.Current.User 匹配,我缺少什麼?
更新:
我意識到我需要一個重定向來正確設置 cookie,我只是無法通過 ajax 呼叫遠端使其工作。這是在站點 B 上執行控制器方法時站點 A 中的腳本的樣子。此重定向不會設置會話。在下一個控制器方法上對使用者進行身份驗證時,它仍然不存在。它只是將我重定向回登錄視圖。
function remoteLogin(id) { $.ajax({ url: "/MyController/RemoteLogin", type: "POST", dataType: "json", data: { "id": id } }).done(function (data) { if (data) { if (data.user) { var user = data.user; $.ajax({ url: "http://siteB.xyz/Account/Login", type: "POST", dataType: "json", data: { "username": user.username, "password": user.password } }).done(function (data) { if (data) { window.location.href = "http://siteB.xyz/Next" } else { alert("Fail."); } }).fail(function (data) { alert("Fail."); }); } else { alert("Fail."); } } }).fail(function (data) { alert("Fail."); }); }
您遇到的問題是此時您只是設置身份驗證 cookie,在表單身份驗證模組中創建的 IPrincipal 在有新請求之前不會發生 - 所以此時 HttpContext.User 處於奇怪的狀態. 一旦發生重定向,因為它是來自瀏覽器的新請求,cookie 將在到達您的頁面並創建正確的使用者對象之前被讀取。
Cookie 僅在請求完成後在瀏覽器上設置。
該問題與您的操作程式碼相對於請求處理管道的執行位置有關。您的程式碼正在ProcessRequest步驟中執行(見下文)。
HttpContext.Current.User應該由AuthenticateRequest事件處理程序設置。 FormsAuthenticationModule負責處理此問題,將Request.Cookies的FormsAuthenticationCookie重新轉換為FormsAuthenticationTicket,然後轉換為IPrincipal。該 Principal 被設置為Request.CurrentUser我相信Thread.CurrentPrincipal
這需要在AuthenticateRequest步驟中完成,因為請求記憶體會因使用者(ResolveRequestCache)而異,而會話狀態總是如此(AcquireRequestState)。此外, AuthenticateRequest 步驟決定在AuthenticateRequest步驟中設置的使用者主體是否具有通過 AuthorizeRequest 步驟所需的權限,而****無需獲得 401 或 300 級重定向到登錄頁面(我相信 MVC 和 WebAPI 的授權機制有效作為ProcessRequest的一部分)
- 開始請求
- AuthenticateRequest (FormsAuthenticationModule)
- AuthorizeRequest(例如,UrlAuthorizationModule)
- 解析請求記憶體
- MapRequestHandler
- 獲取請求狀態
- PreRequestHandlerExecute
- ProcessRequest (HttpHandler, Web Forms, MVC controller actions live here)
您可以嘗試通過將HttpContext.Current.User 和 Thread.CurrentPrincipal設置為您的IPrincipal來強制執行此操作,但它僅適用於在ProcessRequest階段之後執行的程式碼……關於會話狀態、記憶體和授權的重要決定已經製作好了。
理論上,您可以通過編寫 HttpModule 並在AuthenticateRequest (或 global.asax)中/之前實現它來實現類似於您嘗試做的事情,但您還無法訪問更高級別的 MVC 控制器概念,Session狀態等。您可以檢查HttpContext.Request.QueryString、HttpContext.Request.Form和HttpContext.Request.Cookies,但僅此而已。您必須從 HTTP 請求中的 AJAX 呼叫中獲取使用者名,然後呼叫FormsAuthentication.SetAuthCookie()或自己創建FormsAuthenticationTicket和FormsAuthenticationCookie並將 cookie 填充到Request.Cookies和Response.Cookies。當您的控制器邏輯執行時,我很確定該請求將顯示為經過身份驗證。