使用表单身份验证进行模拟
Posted
技术标签:
【中文标题】使用表单身份验证进行模拟【英文标题】:Impersonate using Forms Authentication 【发布时间】:2009-06-30 21:52:30 【问题描述】:我有一个 ASP.NET 站点,它必须使用表单身份验证而不是 Windows 身份验证来访问ActiveDirectoryMembershipProvider
。该站点必须使用表单,因为它们需要设计的输入表单,而不是 Windows 身份验证使用的浏览器身份验证弹出窗口。
该站点需要模拟通过 Active Directory 登录的用户才能访问用户特定的文件。
但是,WindowsIdentity.GetCurrent()
与 HttpContext.Current.User.Identity
不同,尽管我的 web.config 包含:
<authentication mode="Forms">
<forms loginUrl="login.aspx" timeout="480"/>
</authentication>
<identity impersonate="true" />
我无法使用LoginUser()
和WindowsIdentity.Impersonate()
,因为我需要冒充AD 用户以获得他们的特定权限,而且我不知道用户的密码,因为Forms 负责登录。
是否有可能从 login.aspx.cs 中获取System.Web.UI.WebControls.Login.Password
,然后将LoginUser()
令牌保存在会话变量中以供稍后使用WindowsIdentity.Impersonate()
?或者也许是一种更安全的以正确方式模拟的方法?
我很困惑为什么表单身份验证不能自动<identity impersonate="true" />
我已阅读此http://msdn.microsoft.com/en-us/library/ms998351.aspx,但它使用 Windows 身份验证。
【问题讨论】:
这是我使用的解决方法:我授予 IUSER_ 对文件的访问权限,然后通过 DirectorySecurity.GetAccessRules() 检查获取每个文件或文件夹的权限。如果访问规则中有FileSystemAccessRule.Value == "DOMAIN\\"+Page.User.Identity.Name
,那么我将该文件或文件夹添加到列表中。最后我显示文件列表。因此,我没有模拟,而是授予 IUSR_ 完全访问权限,并手动检查我需要该用户访问的内容的权限。
【参考方案1】:
可以使用表单身份验证来模拟用户。以下代码确实有效。
Robert 提到的Visual Studio Magazine article 是一个极好的资源。文章中的示例代码存在一些问题,因此我在下面包含了一些工作代码。
注意:如果您使用的是 Visual Studio,请确保以“以管理员身份运行”启动它,以避免 UAC 阻止模拟出现问题。
// in your login page (hook up to OnAuthenticate event)
protected void LoginControl_Authenticate(object sender, AuthenticateEventArgs e)
int token;
// replace "YOURDOMAIN" with your actual domain name
e.Authenticated = LogonUser(LoginUser.UserName,"YOURDOMAIN",LoginUser.Password,8,0,out token);
if (e.Authenticated)
Session.Add("principal", new WindowsPrincipal(new WindowsIdentity(new IntPtr(token))));
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, out int TokenHandle);
// in global.asax.cs
void Application_PreRequestHandlerExecute(object send, EventArgs e)
if (Thread.CurrentPrincipal.Identity.IsAuthenticated == true && HttpContext.Current.Session != null)
WindowsPrincipal windowsPrincipal = (WindowsPrincipal)Session["principal"];
Session["principal"] = (GenericPrincipal)Thread.CurrentPrincipal;
Thread.CurrentPrincipal = windowsPrincipal;
HttpContext.Current.User = windowsPrincipal;
HttpContext.Current.Items["identity"] = ((WindowsIdentity)windowsPrincipal.Identity).Impersonate();
// in global.asax.cs
void Application_PostRequestHandlerExecute(object send, EventArgs e)
if (HttpContext.Current.Session != null && Session["principal"] as GenericPrincipal != null)
GenericPrincipal genericPrincipal = (GenericPrincipal)Session["principal"];
Session["principal"] = (WindowsPrincipal)Thread.CurrentPrincipal;
Thread.CurrentPrincipal = genericPrincipal;
HttpContext.Current.User = genericPrincipal;
((WindowsImpersonationContext)HttpContext.Current.Items["identity"]).Undo();
// test that impersonation is working (add this and an Asp:Label to a test page)
protected void Page_Load(object sender, EventArgs e)
try
// replace YOURSERVER and YOURDB with your actual server and database names
string connstring = "data source=YOURSERVER;initial catalog=YOURDB;integrated security=True";
using (SqlConnection conn = new SqlConnection(connstring))
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT SUSER_NAME()", conn);
using (SqlDataReader rdr = cmd.ExecuteReader())
rdr.Read();
Label1.Text = "SUSER_NAME() = " + rdr.GetString(0);
catch
更新:
您还应该处理Application_EndRequest
,因为像Response.End()
这样的调用会绕过Application_PostRequestHandlerExecute
。
另一个问题是 WindowsIdentity 可能会被垃圾收集,因此您应该在每次请求时从登录令牌创建一个新的 WindowsIdentity 和 WindowsPrincipal。
更新2:
我不确定为什么这会被否决,因为它有效。我添加了 pinvoke 签名和一些测试代码。同样,使用“以管理员身份运行”启动 Visual Studio。如果您不知道怎么做,请 Google 怎么做。
【讨论】:
FWIW,(我是问题 OP)我赞成这个答案并将其更改为正确答案。 这种方法在我的测试中不适用于 MVC,但我设法从 System.Web.Mvc.Controller 覆盖 BeginExecute 和 EndExecute。这种方法的问题是我需要把它放在 na BaseController 并更改所有现有的控制器以从这个 BaseController 继承 我也在使用 MVC。我不得不注释掉 Session["principal"] = (GenericPrincipal)Thread.CurrentPrincipal;行,因为它无法将 web.PrincipalRole 转换为 GenericPrincipal。但这并不重要,因为我只需要在连接到数据库时模拟一个用户,所以我不需要存储模拟用户以在整个站点中使用。这意味着我也没有在 global.asax.cs 中添加任何内容,我只是将其包含在我的 db 类中。 我还要补充一点,你需要将Application.PostRequestHandlerExecute
和Application.PreRequestHandlerExecute
分别绑定到Application_PostRequestHandlerExecute
和Application_PreRequestHandlerExecute
方法【参考方案2】:
如果您的用户使用的是 IE,那么您可以打开网站的集成安全性,您的用户将通过静默方式进行身份验证(没有登录对话框,没有登录页面)。然后,您的模仿将起作用。如果您需要针对其他浏览器,那么这可能不起作用(用户可能会看到一个登录对话框)。
您当前的模拟将永远无法正常工作,因为您的用户使用域帐户以外的帐户登录。您不能指望该站点冒充未提供其凭据的用户。这将违反基本的安全原则。
【讨论】:
这是一个外联网,因此用户使用AD帐户从局域网外部访问内部文件。使用 ActiveDirectoryMembershipProvider 时,用户使用他们的域帐户登录。实际的用户存储是AD,使用Forms或者Windows认证可以登录成功,但是使用Forms时不能访问文件,只能在使用Windows时访问文件,因为Forms使用IUSR_*账号。【参考方案3】:您可能会发现这很有用:
how to create a login screen that allows Admin users to log in as another user in the user database编辑
在更仔细地阅读您的问题时,我不确定这种方法是否适用于您的场景;当您使用表单身份验证和模拟 Active Directory 用户登录时
【讨论】:
【参考方案4】:我们最近遇到了同样的问题,客户希望他们的用户可以通过 AD 帐户登录,然后必须使用此凭据访问 Analysis Service 以及所有其他数据库。他们希望这样,因为他们实施了审计系统,并且所有访问都必须由当前登录的帐户完成。
我们尝试了 Forms 身份验证和 Win32 LogonUser() API 来模拟部分,它有效,但它也要求我们以纯文本形式输入用户密码。后来,我们决定使用 Windows 身份验证,它为我们节省了大量时间(不再使用 AD 身份验证,手动模拟)。当然,也没有花哨的登录页面。
【讨论】:
【参考方案5】:为了以防万一,有点晚了,我发现了一些对我有用的东西,它真的很简单,但当然只是为了测试目的......
只需使用您的用户名设置一个 cookie。
//Login button. You can give whatever input to the form
protected void Login_Click(object sender, EventArgs e)
FormsAuthentication.SetAuthCookie("your_username", createPersistentCookie: true);
Response.Redirect("~/");
接受任何 cmets...
【讨论】:
这个问题是关于如何让WindowsIdentity.Impersonate()
工作的。我看不出这个答案是如何试图回答这个问题的以上是关于使用表单身份验证进行模拟的主要内容,如果未能解决你的问题,请参考以下文章