4

由于跨受保护的网络共享移动和创建许多文件,我有一个需要进行大量模拟的应用程序。我创建了一个简单的静态类,该类具有一个方法,该方法采用用户、域、密码和一个委托,其中包含您需要在模拟上下文下运行的代码。

我遇到的问题是当 CLR 尝试在此上下文下绑定到引用的程序集时。引发 FileLoadException 并显示“访问被拒绝”消息。

我被引导相信这是由于模拟用户对文件系统下的 *.DLL 文件没有足够的权限。例如,我可以在模拟块之前编写良性代码,除了在上下文切换到模拟用户之前从问题程序集中加载类型外,什么都不做,这很好用!但是,我真的不认为这是一个优雅的解决方案——我是否需要开始担心我可以在模拟下使用哪些类型,并确保我typeof()事先放置了随机语句?

更令人沮丧的是,我没有在本地开发机器上遇到这个问题。当程序集被运送到 beta 环境时,才会出现这个问题。而且我无权从 beta 环境中读取文件权限,无法尝试在我的位置机器上模仿它们。

无论如何,我尝试了这个解决方案:

// Defined above:
// System.Security.Principal.WindowsIdentity identity;
// System.Security.Principal.WindowsImpersonationContext context;

context = identity.Impersonate();

int tries = 0;
while ( true )
{
    try
    {
        contextAction();
    }
    catch ( FileLoadException ex )
    {
        if ( tries > MAX_TRIES )
        {
            // don't allow an infinite loop
            throw;
        }
        if ( String.IsNullOrEmpty( ex.FileName ) )
        {
            // if this is null/empty, we can't really recover
            throw;
        }
        context.Undo(); // return to current logon
        try
        {
            var assemblyName = new AssemblyName( ex.FileName );
            Assembly.Load( assemblyName );
            tries++;
            continue;
        }
        finally
        {
            context = identity.Impersonate(); // re-impersonate
        }
    }   
    finally
    {
        // return to your current windows logon
        context.Undo();
    }
}

没有骰子。我仍然收到“访问被拒绝”异常,除了现在从Assembly.Load.

需要注意的一件有趣的事情是,我从构建服务器中得到了同样的异常。上述解决方案将其固定在构建服务器上。不在我们的测试环境中。

我在这里想念什么?谢谢。

4

3 回答 3

3

我有同样的问题。模拟“会话”似乎没有被正确清理,这似乎是一个问题。在调用逻辑之前加载问题程序集可以正常工作。在撤消和处理模拟身份和上下文后尝试加载程序集会导致另一个 FileLoadException。

另一个线程显示了他们关闭令牌句柄的示例代码,但这样做对我的测试代码没有任何影响。

编辑

在模拟代码之前和之后调用 System.Security.Principal.WindowsIdentity.GetCurrent() 会返回相同的信息,即使必须进行某些更改以不再允许加载问题程序集。

编辑#2(现在有 100% 更多的解决方案!

我们刚刚在我们的网络应用程序中发现了一个单独的、更严重的问题。创建身份和关闭句柄的过程不正确。我们在多线程应用程序中遇到了重大问题,最终导致我们的网站在所有句柄用完时停止运行。在与其他开发人员和在线研究(包括本线程倒数第二​​篇文章中的提示)进行了大量讨论后,我改进了代码,以便正确清理令牌句柄。

这似乎也解决了我们在模拟上下文中加载程序集的问题。无论我多么努力,我现在似乎都无法重现该错误。我将在此处发布我们改进的模拟代码,以便您查看它是否适用于您的应用程序。注意 GetIdentity 中的锁块;这对于多线程应用程序非常重要。

// LogonType = 8        // LOGON32_LOGON_NETWORK_CLEARTEXT
// LogonProvider = 0    // LOGON32_PROVIDER_DEFAULT

[DllImport ( "advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true )]
private static extern bool LogonUser( string userName, string domain,
    string password, int logonType, int logonProvider, ref IntPtr accessToken );

[DllImport ( "kernel32.dll", SetLastError = true )]
private static extern bool CloseHandle( IntPtr handle );

private static readonly object Locker = new Object ();

private static WindowsIdentity GetIdentity( string username, string domain, string password )
{
    lock ( Locker )
    {
        IntPtr token = IntPtr.Zero;
        if ( LogonUser ( username, domain, password,
            (int) LogonType, (int) LogonProvider, ref token ) )
        {
            // using the token to create an instance of WindowsIdentity class
            var identity = new WindowsIdentity ( token );
            CloseHandle ( token ); // the WindowsIdentity object duplicates this token internally
            return identity;
        }

        throw new SecurityException ( string.Format (
            "Invalid username/password (domain: '{0}', username: '{1}')",
            domain, username ) );
    }
}

public static T ExecuteAction<T>( string username, string domain, string password,
    Func<T> contextAction )
{
    var identity = GetIdentity ( username, domain, password );
    var context = identity.Impersonate ();
    try
    {

        return contextAction ();
    }
    finally
    {
        context.Undo ();
        context.Dispose ();
    }
}
于 2009-10-07T20:17:03.117 回答
0

我不确定问题是什么,但是当从非管理员用户帐户模拟管理员时,Windows 模拟无法正常工作。可能是您的最终用户不是管理员,当您尝试模拟时,它失败导致文件访问错误。

于 2009-09-30T17:33:58.640 回答
0

您可以将“违规”程序集放在 GAC 中。

或者在模拟用户之前为要模拟的用户在包含程序集的目录上动态添加可继承的读取和执行权限。我假设您控制有问题的程序集的位置?

于 2009-11-21T19:02:28.517 回答