我有一个经典的ASP页面 - 用JScript编写 - 使用Scripting.FileSystemObject将文件保存到网络共享 - 而且它不起作用。 (“许可被拒绝”)
使用Windows身份验证在IIS下运行ASP页面,并启用模拟。
如果我通过CScript.exe在本地运行以下代码块:
var objNet = new ActiveXObject("WScript.Network");
WScript.Echo(objNet.ComputerName);
WScript.Echo(objNet.UserName);
WScript.Echo(objNet.UserDomain);
var fso = new ActiveXObject("Scripting.FileSystemObject");
var path = "\\\\myserver\\my_share\\some_path";
if (fso.FolderExists(path)) {
WScript.Echo("Yes");
} else {
WScript.Echo("No");
}
我得到(预期)输出:
MY_COMPUTER
dylan.beattie
MYDOMAIN
Yes
如果我在.ASP页面中运行相同的代码,则将Response.Write替换为WScript.Echo我得到此输出:
MY_COMPUTER
dylan.beattie
MYDOMAIN
No
现在 - 我的理解是WScript.Network对象将检索实际运行代码的线程的当前安全凭证。如果这是正确的 - 那么为什么在同一域上的同一用户从CScript.exe与ASP获得不同的结果?如果我的ASP代码 以dylan.beattie运行,那为什么我看不到网络共享?如果不以dylan.beattie身份运行,为什么WScript.Network会认为它是什么?
答案 0 :(得分:4)
在模拟下,您只能访问本地计算机上无法通过网络访问任何内容的安全资源。
在Windows上作为模拟用户运行时,您在所谓的网络令牌下运行。此令牌具有用户的本地计算机访问凭据,但没有远程访问凭据。因此,当您访问网络共享时,您实际上是以匿名用户身份访问它。
当您在桌面上运行进程(如CScript.exe)时,您将在交互式用户令牌下运行。此令牌具有本地和远程访问的完整凭据,因此您可以访问网络共享。
要在模拟Windows用户时访问远程资源,您必须使用委派而不是模拟。这将涉及对Active目录的一些更改,以允许对您的域中的计算机和/或用户进行委派。这可能存在安全风险,因此应仔细审核。
答案 1 :(得分:4)
你的问题很清楚。在当前实现中,您只能模拟用户而无需委派。我不想重复斯蒂芬马丁已经写过的信息。我只想添加至少三个解决方案。斯蒂芬马丁建议的典型授权方式只有一种方式。您可以在此处阅读更多方法:http://msdn.microsoft.com/en-us/library/ff647404.aspx#paght000023_delegation。我看到了解决问题的三种实用方法:
将用户的模拟令牌转换为具有模拟委派级别或新主要令牌的令牌。您可以对DuplicateToken
或DuplicateTokenEx
。
使用 S4U2Self (请参阅http://msdn.microsoft.com/en-us/magazine/cc188757.aspx和http://msdn.microsoft.com/en-us/library/ms998355.aspx)从一个简单的.NET语句{{1}接收新旧令牌}
您可以访问一个固定帐户的其他服务器。它可以是IIS应用程序池帐户上的计算机帐户。它可以是另一个固定定义的帐户,只能用于访问文件系统。
了解运行IIS的服务器上的Windows Server版本以及域中Active Directory中的域功能级别非常重要(如果您在“Active Directory域和信任”工具中看到此内容)选择您的域并选择“提升域功能级别”。了解IIS的应用程序池在哪个帐户下运行也很有趣。
第一种和第三种方式永远有效。第三种方式可能对您的环境和文件系统中的当前权限有害。第二个非常优雅。它允许控制从IIS访问哪些服务器(文件服务器)。这种方式有一些限制,需要在Active Directory中完成一些工作。
因为您使用的是经典ASP,所以必须创建一个小的可编写脚本的软件组件来支持您的实现。
您更喜欢哪种方式?
UPDATED 基于评论中的问题:因为您使用的是经典ASP,所以不能直接使用Win32 API,但您可以在VB6或.NET中编写一个使用API的小型COM组件。你需要。例如,您可以使用http://support.microsoft.com/kb/248187/en中的代码。但你应该在里面做一些其他事情。所以我现在解释一下哪个Win32 API可以帮助你做所有你需要的令牌和模仿。
首先是关于冒充的一个小解释。一切都很容易。始终有一个主令牌运行该进程。对于任何线程,可以分配另一个令牌(线程令牌)。要执行此操作,需要拥有用户WindowsIdentity wi = new WindowsIdentity(identity);
的令牌并调用API hUserToken
。
要返回原始进程令牌(仅适用于当前线程),您可以调用ImpersonateLoggedOnUser(hUserToken);
函数。 IIS将收到用户令牌并已为您模拟,因为您已配置了您的网站。要返回原始进程令牌,您应该在自定义COM组件中实现函数RevertToSelf()
的调用。也许,如果你不需要在ASP页面中做更多的事情,那就足够了,但我建议你在使用文件操作之前要更加小心并将当前用户令牌保存在变量中。然后使用文件系统进行所有操作,最后将用户令牌重新分配回当前线程。您可以将模拟令牌分配给RevertToSelf()
的线程。要提供(保存)当前线程令牌(在您的情况下为用户令牌),您可以使用SetThreadToken(NULL,hUserToken);
API。它必须工作。
更新 2:可能在一个ASP页面的末尾使用OpenThreadToken
功能对您来说已经不错了。相应的C#代码可以是这样的:
在“类库”类型的C#中创建一个名为RevertToSelf()
的新项目。将以下代码粘贴到
LoginAdmin
签入“构建”部分“注册COM互操作”中的项目属性。在项目的“签名”部分检查签署程序集并在“选择强名称密钥文件”中选择using System;
using System.Runtime.InteropServices;
namespace LoginAdmin {
[InterfaceTypeAttribute (ComInterfaceType.InterfaceIsDual)]
public interface IUserImpersonate {
[DispId(1)]
bool RevertToSelf ();
}
internal static class NativeMethods {
[DllImport ("advapi32.dll", SetLastError = true)]
internal static extern bool RevertToSelf ();
}
[ClassInterface (ClassInterfaceType.AutoDual)]
public class UserImpersonate : IUserImpersonate {
public UserImpersonate () { }
public bool RevertToSelf () {
return NativeMethods.RevertToSelf();
}
}
}
,然后键入任何文件名和密码(或选中“保护我的密钥...”)。最后,您应该在项目的Properties部分中的AssemblyInfo.cs中修改一行:
<New...>
编译此项目后,您将获得两个文件:LoginAdmin.dll和LoginAdmin.tlb。 DLL已在当前计算机上注册。要在另一台计算机上注册,请使用 RegAsm.exe 。
要在ASP页面上测试此COM DLL,您可以执行以下操作
[assembly: ComVisible (true)]
如果用于运行IIS应用程序池的帐户可以访问相应的网络共享,则输出将如下所示
<%@ Language="javascript" %>
<html><body>
<% var objNet = Server.CreateObject("WScript.Network");
Response.Write("Current user: ");Response.Write(objNet.UserName);Response.Write("<br/>");
Response.Write("Current user's domain: ");Response.Write(objNet.UserDomain);Response.Write("<br/>");
var objLoginAdmin = Server.CreateObject("LoginAdmin.UserImpersonate");
var isOK = objLoginAdmin.RevertToSelf();
if (isOK)
Response.Write("RevertToSelf return true<br/>");
else
Response.Write("RevertToSelf return false<br/>");
Response.Write("One more time after RevertToSelf()<br/>");
Response.Write("Current user: ");Response.Write(objNet.UserName);Response.Write("<br/>");
Response.Write("Current user's domain: ");Response.Write(objNet.UserDomain);Response.Write("<br/>");
var fso = Server.CreateObject("Scripting.FileSystemObject");
var path = "\\\\mk01\\C\\Oleg";
if (fso.FolderExists(path)) {
Response.Write("Yes");
} else {
Response.Write("No");
}%>
</body></html>