Asp.net 使用表单身份验证模拟

Asp.net 使用表单身份验证模拟,asp.net,active-directory,forms-authentication,impersonation,windows-authentication,Asp.net,Active Directory,Forms Authentication,Impersonation,Windows Authentication,我有一个ASP.NET站点,它必须使用窗体身份验证而不是Windows身份验证来访问ActiveDirectoryMembershipProvider。网站必须使用表单,因为它们需要设计的输入表单,而不是Windows身份验证使用的浏览器身份验证弹出窗口 该站点需要模拟通过Active Directory登录的用户以访问用户特定的文件 但是,WindowsIdentity.GetCurrent()与HttpContext.Current.User.Identity不同,尽管my web.conf

我有一个ASP.NET站点,它必须使用窗体身份验证而不是Windows身份验证来访问
ActiveDirectoryMembershipProvider
。网站必须使用表单,因为它们需要设计的输入表单,而不是Windows身份验证使用的浏览器身份验证弹出窗口

该站点需要模拟通过Active Directory登录的用户以访问用户特定的文件

但是,
WindowsIdentity.GetCurrent()
HttpContext.Current.User.Identity
不同,尽管my web.config包含:

<authentication mode="Forms">
    <forms loginUrl="login.aspx" timeout="480"/>
</authentication>
<identity impersonate="true" />

但它使用Windows身份验证。

如果您的用户正在使用IE,则您可以打开网站的集成安全性,您的用户将以静默方式进行身份验证(无登录对话框,无登录页面)。你的模仿就可以了。如果您需要针对其他浏览器,那么这可能不起作用(用户可能会看到一个登录对话框)


您当前的模拟将永远无法工作,因为您的用户正在使用其域帐户以外的帐户登录。您不能期望站点模拟未提供其凭据的用户。这与基本的安全原则背道而驰。

您可能会发现这很有用:

编辑


仔细阅读你的问题后,我不确定这种方法是否适用于你的情景;当您使用表单身份验证和模拟Active Directory用户登录时,我们最近遇到了相同的问题,客户希望他们的用户可以通过AD帐户登录,然后必须使用此凭据访问Analysis Service以及所有其他数据库。他们希望这样做,因为他们实现了一个审计系统,所有访问都必须由当前登录帐户完成


我们尝试了表单身份验证和Win32 LogonUser()API来模拟部分,虽然有效,但它也要求我们以明文形式输入用户密码。后来,我们决定使用Windows身份验证,这为我们节省了很多时间(不再进行广告身份验证,手动模拟)。当然,也没有花哨的登录页面。

可以使用表单身份验证模拟用户。下面的代码确实有效

罗伯特提到的是一个很好的资源。本文中的示例代码存在一些问题,因此我在下面包含了一些工作代码

注意:如果您使用的是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\u EndRequest
,因为像
Response.End()
这样的调用将绕过
Application\u PostRequestHandlerExecute

另一个问题是WindowsIdentity可能会被垃圾收集,因此您应该在每次请求时从登录令牌创建新的WindowsIdentity和WindowsPrincipal

更新2:


我不知道为什么这会被否决,因为它是有效的。我添加了pinvoke签名和一些测试代码。再次使用“以管理员身份运行”启动Visual Studio。如果你不知道怎么做,谷歌怎么做。

为了以防万一,而且有点晚了,我找到了适合我的东西,它非常简单,但当然只是为了测试目的

只要用你的用户名设置一个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("~/");
}

接受任何评论…

这是一个外联网,因此用户使用局域网外的AD帐户访问内部文件。使用ActiveDirectoryMembershipProvider时,用户使用其域帐户登录。实际的用户存储是AD,他们可以使用窗体或Windows身份验证成功登录,但在使用窗体时无法访问文件,只有在使用Windows时,因为窗体使用IUSR_u*帐户。以下是我使用的解决方法:我给了IUSER_uu访问文件的权限,然后我通过DirectorySecurity.GetAccessRules()检查并获取每个文件或文件夹的权限。如果访问规则中的
FileSystemAccessRule.Value==“DOMAIN\\”+Page.User.Identity.Name
,则我将该文件或文件夹添加到列表中。最后,我显示文件列表。因此,我没有模拟,而是授予IUSR_uuu完全访问权限,并手动检查我需要该用户访问的内容的权限。FWIW,(我是问题OP)我对这个答案进行了投票,并将其更改为正确答案。这种方法在我的测试中对MVC不起作用,但我成功地从System.Web.Mvc.Controller重写了BeginExecute和EndExecute。这种方法的问题是,我需要将它放在na BaseController中,并将所有现有的控制器更改为继承自此BaseControllerI,我也使用MVC。我不得不注释掉会话[“主体”]=(GenericPrincipal)Thread.CurrentPrincipal;行,因为它无法将web.PrincipalRole转换为GenericPrincipal。但这并不重要,因为我只需要在连接到数据库时模拟用户,所以我不需要存储模拟用户以供整个站点使用。这意味着我也没有在global.asax.cs中放入任何内容,我只是将其包含在我的db类中。我还想补充一点,您需要绑定
应用程序。PostRequestHandlerExecute
应用程序。PreRequestHandlerExecute
应用程序\u PostRequestHandlerExecute
Application\u prerequesthandler分别执行
方法此问题是关于如何使
WindowsIdentity.Impersonate()
工作的。我不明白这个答案是如何试图回答这个问题的