先在Web.config里设置成Form认证:
<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" name=".ASPXFORMSAUTH" />
</authentication>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
当然还得使用Session:
<sessionState mode="InProc"/>
以及配置站点/虚拟目录为匿名认证
然后是一个身份模拟的辅助类:
public class ImpersonateHelper
{
public const string SystemUserToken = "SystemUserToken";
public ImpersonateHelper()
{}
[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
public static IntPtr LogonUser(string userName, string password)
{
string[] userInfo = userName.Split('//');
IntPtr tokenHandle = IntPtr.Zero;
IntPtr dupeTokenHandle = new IntPtr(0);
const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userInfo[1], userInfo[0], password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref tokenHandle);
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
return tokenHandle;
}
public static void Impersonate(IntPtr token)
{
WindowsIdentity identity = new WindowsIdentity(token);
identity.Impersonate();
///WindowsImpersonationContext impersonatedUser = newId.Impersonate();
///WindowsImpersonationContext
}
private const string TokenCollection = "TokenCollection";
public static void StoreUserToken(System.IntPtr token)
{
System.Web.HttpContext context = System.Web.HttpContext.Current;
object o = context.Application[TokenCollection];
if (o == null)
{
o = new System.Collections.Hashtable();
System.Web.HttpContext.Current.Application[TokenCollection] = o;
}
System.Collections.Hashtable tokens = o as System.Collections.Hashtable;
tokens[context.Request["ASP.NET_SessionId"]] = token;
}
public static void RemoveToken(string sessionId)
{
System.Web.HttpContext context = System.Web.HttpContext.Current;
object o = context.Application[TokenCollection];
if (o != null)
{
System.Collections.Hashtable tokens = o as System.Collections.Hashtable;
if (tokens.ContainsKey(sessionId))
{
tokens.Remove(sessionId);
}
}
}
public static System.IntPtr GetCurrentUserToken()
{
System.Web.HttpContext context = System.Web.HttpContext.Current;
object o = context.Application[TokenCollection];
if (o == null)
{
return System.IntPtr.Zero;
}
System.Collections.Hashtable tokens = o as System.Collections.Hashtable;
string sessionId = context.Request["ASP.NET_SessionId"];
if (sessionId == null || sessionId == "")
{
return System.IntPtr.Zero;
}
object token = tokens[sessionId];
if (token == null)
{
return System.IntPtr.Zero;
}
return (System.IntPtr)token;
}
}
在Login.aspx的Login的Click事件通过调用Windows API获取登陆凭据,并按SessionID为键值保存在Application中,接着模拟身份并转向原请求的页面
System.IntPtr token = ImpersonateHelper.LogonUser(userName, password);
ImpersonateHelper.StoreUserToken(token);
ImpersonateHelper.Impersonate(token);
FormsAuthentication.RedirectFromLoginPage(userName, false);
然后在Global.asax.cs中的BeginRequest时取回与当前Session相关的Windows凭据,接着模拟成该身
protected void Application_BeginRequest(Object sender, EventArgs e)
{
System.IntPtr token = ImpersonateHelper.GetCurrentUserToken();
if (token == System.IntPtr.Zero)
{
System.Web.HttpContext context = System.Web.HttpContext.Current;
string currentUrl = context.Request.Url.ToString();
if (currentUrl.ToLower().IndexOf("/login.aspx?returnurl=") == -1)
{
context.Response.Redirect("~/Login.aspx?ReturnUrl=loader.aspx");
}
}
else
{
ImpersonateHelper.Impersonate(token);
}
}
最后一步,防止用户在Session超时后仍使用原SessionID,注:ASP.net 2.0 中可以通过Web.Config配制成当Session超时后强制生成新的SessionId
protected void Session_Start(Object sender, EventArgs e)
{
if (Session.IsNewSession)
{
ImpersonateHelper.RemoveToken(Session.SessionID);
}
}