我還沒有發現任何「最佳實踐」在這種情況下寫上去,但這裏是我已經解決:
1)爲了支持匿名用戶(即SQL數據庫中的項目之間切換)我只需使用會話變量來跟蹤項目選擇。我有一個全局屬性,它使用此項目選擇來在需要時提供相應的SQL連接字符串。
2)爲了支持在應用了角色限制的頁面上對GetRolesForUser()的調用,我們不能使用會話變量,因爲如前所述,當實際調用GetRolesForUser()時,會話變量尚未初始化(並且我沒有辦法在請求週期的這個早期階段迫使它進入)。
3)唯一的選擇是使用cookie,或者使用Forms Authentication工單的UserData字段。我瀏覽了很多有關使用會話/ cookie/ID鏈接到應用程序緩存中存儲的對象的理論(當會話不可用時可用),但最終正確的選擇是將此數據放在身份驗證票證中。
4)如果用戶登錄到一個項目,它通過一個ProjectName/UserName對,因此我們在任何地方跟蹤用戶的身份驗證,我們都需要這些數據。在微不足道的測試中,我們可以使用單獨的cookie中的用戶名和單獨的cookie中的項目名稱,但它們可能會不同步。例如,如果我們在項目名稱中使用會話cookie,並在登錄時勾選「記住我」(爲身份驗證票證創建永久性cookie),那麼當會話cookie過期時(瀏覽器關閉時,我們可以以用戶名結尾但沒有項目名稱)。因此,我手動將項目名稱添加到身份驗證票證的UserData字段。
5)我還沒有弄清楚如何在不明確設置cookie的情況下操作UserData字段,這意味着我的解決方案無法在「無Cookie」會話模式下工作。
最終的代碼證明是相對簡單的。
我重寫一個LoginView在登錄頁面的身份驗證事件:
//
// Add project name as UserData to the authentication ticket.
// This is especially important regarding the "Remembe Me" cookie - when the authentication
// is remembered we need to know the project and user name, otherwise we end up trying to
// use the default project instead of the one the user actually logged on to.
//
// http://msdn.microsoft.com/en-us/library/kybcs83h.aspx
// http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.login.remembermeset(v=vs.100).aspx
// http://www.hanselman.com/blog/AccessingTheASPNETFormsAuthenticationTimeoutValue.aspx
// http://www.csharpaspnetarticles.com/2009/02/formsauthentication-ticket-roles-aspnet.html
// http://www.hanselman.com/blog/HowToGetCookielessFormsAuthenticationToWorkWithSelfissuedFormsAuthenticationTicketsAndCustomUserData.aspx
// http://stackoverflow.com/questions/262636/cant-set-formsauthenicationticket-userdata-in-cookieless-mode
//
protected void LoginUser_Authenticate(object sender, AuthenticateEventArgs e)
{
string userName = LoginUser.UserName;
string password = LoginUser.Password;
bool rememberMe = LoginUser.RememberMeSet;
if ([ValidateUser(userName, password)])
{
// Create the Forms Authentication Ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
userName,
DateTime.Now,
DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),
rememberMe,
[ ProjectName ],
FormsAuthentication.FormsCookiePath);
// Create the encrypted cookie
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
if (rememberMe)
cookie.Expires = DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes);
// Add the cookie to user browser
Response.Cookies.Set(cookie);
// Redirect back to original URL
// Note: the parameters to GetRedirectUrl are ignored/irrelevant
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, rememberMe));
}
}
我有這樣的全球性方法來返回項目名稱:
/// <summary>
/// SQL Server database name of the currently selected project.
/// This name is merged into the connection string in EventConnectionString.
/// </summary>
public static string ProjectName
{
get
{
String _ProjectName = null;
// See if we have it already
if (HttpContext.Current.Items["ProjectName"] != null)
{
_ProjectName = (String)HttpContext.Current.Items["ProjectName"];
}
// Only have to do this once in each request
if (String.IsNullOrEmpty(_ProjectName))
{
// Do we have it in the authentication ticket?
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = identity.Ticket;
_ProjectName = ticket.UserData;
}
}
}
// Do we have it in the session (user not logged in yet)
if (String.IsNullOrEmpty(_ProjectName))
{
if (HttpContext.Current.Session != null)
{
_ProjectName = (string)HttpContext.Current.Session["ProjectName"];
}
}
// Default to the test project
if (String.IsNullOrEmpty(_ProjectName))
{
_ProjectName = "Test_Project";
}
// Place it in current items so we do not have to figure it out again
HttpContext.Current.Items["ProjectName"] = _ProjectName;
}
return _ProjectName;
}
set
{
HttpContext.Current.Items["ProjectName"] = value;
if (HttpContext.Current.Session != null)
{
HttpContext.Current.Session["ProjectName"] = value;
}
}
}
你在哪裏設置的驗證?如果有必要,我傾向於在會員供應商中設置會話,並且通常會起作用。 –
當RoleProvider.GetRolesForUser()被調用時,我發現HttpContext.Current.Sessions爲null,這意味着我不知道要查找哪個數據庫以獲取用戶詳細信息。 – Etherman
我已經解決了使用cookie的主要問題。而不是將項目名稱保存到Session [],我將它保存到Response.Cookies [],然後在GetRolesForUser()中使用HttpContext.Current.Request.Cookies [「ProjectName」] .value值。對於我目前的目的來說,這似乎有效。不過,我看到這個中斷的無Cookie功能。 – Etherman