ОБНОВЛЕНИЕ: в наши дни я решаю эту проблему гораздо проще, используя сертификат SSL с подстановочными знаками, который позволяет мне настраивать субдомены для каждого проекта, таким образом, выбор проекта указывается непосредственно в URL (и каждый проект получает свой собственный субдомен),Я по-прежнему использую cookie-файлы исключительно для тестирования при работе на локальном хосте, где у нас нет поддоменов.
Исходное решение:
Я не нашел ни одной "наилучшей практики", описывающей этот сценарий, но вот что я остановился на:
1) ЧтобыПоддержка анонимных пользователей, переключающихся между проектами (например, базы данных SQL). Я просто использую переменную сеанса для отслеживания выбора проекта.У меня есть глобальное свойство, которое использует этот выбор проекта для обслуживания соответствующей строки соединения SQL, как и когда это требуется.
2) Для поддержки вызова GetRolesForUser () на страницах, к которым применяются ограничения роли.из них мы не можем использовать переменную сеанса, потому что, как указано, переменная сеанса еще не была инициализирована, когда фактически вызывается GetRolesForUser () (и я не нашел способа заставить ее существовать на этом раннем этапе цикла запроса).
3) Единственный вариант - использовать файл cookie или поле UserData билета проверки подлинности с помощью форм.Я пролистал множество теорий об использовании сеанса / cookie / идентификаторов, связанных с объектом, хранящимся в кэше приложения (который доступен, когда сеанс недоступен), но в конечном итоге правильный выбор - поместить эти данные в билет аутентификации.
4) Если пользователь вошел в проект с помощью пары ProjectName / UserName, следовательно, где бы мы ни отслеживали аутентификацию пользователя, нам требуются обе эти данные.В обычном тестировании мы можем избежать использования имени пользователя в заявке и имени проекта в отдельном файле cookie, однако они могут быть не синхронизированы.Например, если мы используем файл cookie сеанса для имени проекта и ставим галочку «запомнить меня» при входе в систему (создавая постоянный файл cookie для билета проверки подлинности), мы можем получить имя пользователя, но не имя проекта, когда срок действия файла cookie сеанса истечет (браузер закрыт).Поэтому я вручную добавляю имя проекта в поле UserData билета аутентификации.
5) Я не понял, как манипулировать полем UserData без явной установки cookie, что означает, что мое решение не может работать в "режим без cookie.
Окончательный код оказался относительно простым.
Я переопределяю событие Authenticate в 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
// /170824/ne-udaetsya-ustanovit-formsauthenicationticket-userdata-v-rezhime-bez-failov-cookie
//
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));
}
}
Iесть этот глобальный метод для возврата имени проекта:
/// <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;
}
}
}