被這個問題困擾多年,今天終於找到了更簡單的解決方法,分享一下。
問題場景
假設我們在i.cnblogs.com站點的web.config中對FormsAuthentication進行了如下的設置:
<authentication mode="Forms">
<forms name=".cnblogs" loginUrl="https://passport.cnblogs.com/login.aspx" protection="All" path="/"/>
</authentication>
當我們訪問一個需要登錄後才能訪問的URL時,比如:http://i.cnblogs.com/post/list,請求會被重定向至如下的地址:
https://passport.cnblogs.com/login.aspx?ReturnUrl=%2fpost%2flist
瞧!通過ReturnUrl查詢參數傳遞給登錄頁面的是相對路徑——這就是問題所在。由於訪問的頁面與登錄頁面不在同一個二級域名下,使用這個相對路徑是Return不回來的。
問題的根源
用ILSPY看一下System.Web.Security.FormsAuthentication的代碼,立馬就能知道問題原因所在:
internal static string GetLoginPage(string extraQueryString, bool reuseReturnUrl) { //... if (text2 == null) { text2 = HttpUtility.UrlEncode(current.Request.RawUrl, current.Request.ContentEncoding); } text = text + FormsAuthentication.ReturnUrlVar + "=" + text2; if (!string.IsNullOrEmpty(extraQueryString)) { text = text + "&" + extraQueryString; } return text; }
由碼可見,微軟根本就無視了登錄頁面不在同一個二級域名的基本應用場景,而且一直無視到現在。
以前的解決方法
在當前站點添加一個中轉頁面,由中轉頁面重定向至登錄頁面。
於是,web.config的設置變成了如下的樣子,先重定向至當前站點的登錄中轉頁面。
<authentication mode="Forms">
<forms name=".cnblogs" loginUrl="~/account/login" protection="All" path="/"/>
</authentication>
然後,在中轉頁面使用絕對路徑作為ReturnUrl的值,再重定向至真正的登錄頁面。
中轉頁面的示例代碼如下:
public class AccountController : Controller { public ActionResult Login(string ReturnUrl) { return Redirect("https://passport.cnblogs.com/login.aspx?ReturnUrl=" + HttpUtility.UrlEncode("http://" + Request.Url.Host) + ReturnUrl); } }
雖然解決了問題,但是對於這樣的解決方法,我覺得有些啰嗦,總覺得有更好的解決方法,可是一直沒找到。
今天再次面對這個問題時,狠了一下心,竟然有了意外的收獲!
更簡單的解決方法
Forms驗證中,工作在第一線、最苦最累的是System.Web.Security.FormsAuthenticationModule。
它在OnEnter(object source, EventArgs eventArgs)中調用了OnAuthenticate方法:
// System.Web.Security.FormsAuthenticationModule private void OnEnter(object source, EventArgs eventArgs) { //... this.OnAuthenticate(new FormsAuthenticationEventArgs(context)); //... }
而在OnAuthenticate()方法中有如下的事件處理:
private void OnAuthenticate(FormsAuthenticationEventArgs e) { HttpCookie httpCookie = null; if (this._eventHandler != null) { this._eventHandler(this, e); } //... }