跨多个项目的ASP.NET用户身份验证
本文关键字:NET 用户 身份验证 ASP 项目 | 更新日期: 2023-09-27 18:21:12
我正在现有系统上构建ASP.NET UI,该系统由每个项目的独立SQL server数据库组成。"企业"数据库列出了所有当前项目,允许匿名用户选择要工作的项目。项目名称存储在会话变量中。当需要登录时,会从项目名称指示的数据库中获取用户名/密码/角色等。为此,我实现了自己的基本成员资格和角色提供程序,并在web.config中进行了更改,以指定特定页面所需的角色。(我不使用标准的ASP.NET配置工具来管理用户,我有可以使用我的用户表的现有应用程序)。
这一切最初似乎都起作用,但我发现,当授权系统检查当前用户所属的角色以确定页面是否可访问时,会话变量尚未加载。因此,如果我们有一个<在web.config中允许角色="xxx">,则在加载会话数据之前,也就是在我知道应该使用哪个项目数据库之前,授权系统就会启动。
[特别是:当调用RoleProvider.GetRolesForUser时,HttpContext.Current.Session为null]
任何解决过这个问题的人都应该清楚我在说什么。因此,我的问题是:
A) 这种情况的"最佳实践"解决方案是什么?
B) 我是否可以将项目名称存储在授权阶段可用的其他地方(不在会话变量中)?
[更新:是的-我们可以使用cookie,假设我们不需要无cookie操作]
C) 有没有办法在早些时候手动获取会话变量?
我尝试了在cookie中缓存角色的选项,但在上用该选项测试了几分钟后,我发现GetRolesForUsers仍在被调用。
感谢
更新:
以下是对根问题的另一个描述,建议"应用程序可以将此信息缓存在cache或application对象中。":
http://connect.microsoft.com/VisualStudio/feedback/details/104452/session-is-null-in-call-to-getrolesforuser
更新:
这看起来与此处发现的问题相同:
扩展RoleProvider GetRolesForUser()
更新:
有人建议在FormsAuthenticationTicket中使用UserData,但即使没有登录,我也需要这些数据。
UPDATE:这些天我用一种更简单的方式解决了这个问题,使用通配符SSL证书,我可以为每个项目配置子域,因此项目选择直接在URL中指定(每个项目都有自己的子域)。当在没有子域的localhost上运行时,我仍然使用cookie破解纯粹用于测试目的。
原始解决方案:
我还没有找到任何关于这种情况的"最佳实践",但以下是我已经确定的:
1) 为了支持匿名用户在项目(即SQL数据库)之间切换,我只需使用会话变量来跟踪项目选择。我有一个全局属性,它使用此项目选择来在需要时提供相应的SQL连接字符串。
2) 为了在应用了角色限制的页面上支持对GetRolesForUser()的调用,我们不能使用会话变量,因为如上所述,当实际调用GetRolesFor User()时,会话变量尚未初始化(在请求周期的早期,我找不到强制它存在的方法)。
3) 唯一的选项是使用cookie,或者使用Forms Authentication票证的UserData字段。我浏览了许多关于使用链接到存储在应用程序缓存中的对象的会话/cookie/ID的理论(当会话不可用时,该对象可用),但最终正确的选择是将这些数据放在身份验证票证中。
4) 如果用户是通过ProjectName/UserName对登录到项目的,因此在跟踪用户身份验证的任何地方,我们都需要这两个数据。在琐碎的测试中,我们可以在票证中使用用户名,在单独的cookie中使用项目名称,但这些可能会不同步。例如,如果我们使用会话cookie作为项目名称,并在登录时勾选"记住我"(为身份验证票证创建永久cookie),那么当会话cookie过期(浏览器关闭)时,我们最终可能会使用用户名,但没有项目名称。因此,我手动将项目名称添加到身份验证票证的UserData字段中。
5) 我还没有弄清楚如何在不显式设置cookie的情况下操作UserData字段,这意味着我的解决方案无法在"无cookie"会话模式下工作。
最后的代码变得相对简单。
我在登录页面中覆盖LoginView的Authenticate事件:
//
// 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;
}
}
}
您不能将项目选择回发到某个页面,将该选择添加到会话,然后重定向到适当的受保护页面,在那里auth将启动并强制登录吗?
ASP.NET会话不会以cookie的形式创建,除非您在其中放置至少一个项。