我陷入了一种非常奇怪的境地。
我有一个工作流程,用于在我的网络应用程序上配置新网站。此工作流使用一个自定义工作流活动来使用后续语句来配置站点。
---为了清楚起见省略了其他代码----
SPSiteCollection.Add()
当我的应用程序池帐户与Central Admin应用程序池帐户不同时,此语句会抛出followign异常。
经过大量的谷歌搜索和调查结果后,我已将其归结为Applicaiton Pool帐户权限。访问被拒绝。 (HRESULT异常:0x80070005(E_ACCESSDENIED)) 在 Microsoft.SharePoint.SPGlobal.HandleUnauthorizedAccessException(UnauthorizedAccessException ex)at Microsoft.SharePoint.Library.SPRequest.CreateSite(GUID gApplicationId,String bstrUrl,Int32 lZone,Guid gSiteId,Guid gDatabaseId, String bstrDat
工作流代码始终在系统帐户(应用程序池标识)下运行。为了创建新的SharePoint网站集,应用程序池需要访问“SharePoint_Config”数据库。
当我的Web应用程序在Central Admin的应用程序池凭据下运行时,它具有对配置数据库的所有访问权限。但是当我在任何其他具有较少权限的应用程序池身份下运行时。即使我将DBO权限授予配置数据库中的应用程序池帐户,它也会引发异常。
我的应用程序事件日志包含以下条目: -
事件来源:Windows SharePoint 服务3事件类别:数据库 事件ID:3760日期:2010年2月3日 时间:上午2:36:16用户:N / A. 电脑:SHAREPOINT20描述: SQL数据库'SharePoint_Config'上 SQL Server实例'houspsr001'没有 找到。其他错误信息 来自SQL Server的内容包含在下面。
无法打开数据库 请求“SharePoint_Config” 登录。登录失败。登录失败 对于用户'DOMAIN \ WebAppPool'。
有关详细信息,请参阅“帮助和” 支持中心在 http://go.microsoft.com/fwlink/events.asp
我的问题是......在中央管理员的应用程序池帐户下运行此类代码是否可行。
对此有任何解决方法....?
我的问题
答案 0 :(得分:1)
最后,访问被拒绝的问题已经解决。正如我在上一封电子邮件中提出的那样,问题是由于对我的应用程序池标识的许可不足。
我的工作流程使用ElevatedPrevilages来设置网站集,并且它曾经从数据库中获取Access Denied,因为它没有修改SharePoint_Config数据库的权限。
分辨率 为了解决此问题,我必须模拟Central Admin的应用程序池标识。以下是模拟Central Admin应用程序池用户所需的方法。
#region Application Pool Identity Impersonate
protected static WindowsIdentity CreateIdentity(string User, string Domain, string Password)
{
// The Windows NT user token.
IntPtr tokenHandle = new IntPtr(0);
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_NETWORK = 3;
tokenHandle = IntPtr.Zero;
// Call LogonUser to obtain a handle to an access token.
int returnValue = LogonUser(User, Domain, Password,LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,out tokenHandle);
//Check if the logon user method succeeded
if (returnValue <= 0)
{
int ret = Marshal.GetLastWin32Error();
throw new Exception("LogonUser failed with error code: " + ret);
}
//System.Diagnostics.Debug.WriteLine("Created user token: " + tokenHandle);
//The WindowsIdentity class makes a new copy of the token.
//It also handles calling CloseHandle for the copy.
WindowsIdentity id = new WindowsIdentity(tokenHandle);
CloseHandle(tokenHandle);
return id;
}
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken
);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int ImpersonateLoggedOnUser(
IntPtr hToken
);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RevertToSelf();
[DllImport("kernel32.dll", SetLastError = true)]
static extern int CloseHandle(IntPtr hObject);
#endregion
然后我创建网站集的代码如下: -
//Impersonate the logged in user, ApplicationUser, LoginDomain and Password are exposed as property of the class.
WindowsImpersonationContext wiContext = CreateIdentity(this.ApplicationPoolUser, this.LoginDomain, this.SystemPassword).Impersonate();
//Provision new site collection and update the property for new site collection url.
using (SPSite newSiteCollection = spSiteColl.Add(SUGGESTEDURL, TITLE, DESC, LCID, WEBTEMPLATE, PRIMARYOWNER.LoginName, PRIMARYOWNER.Name, PRIMARYOWNER.Email, SECONDARYOWNER.LoginName, SECONDARYOWNER.Name, SECONDARYOWNER.Email))
{
this.SUGGESTEDURL = newSiteCollection.Url;
}
//Reset the impersonation.
wiContext.Undo();
答案 1 :(得分:1)
由于我不允许对Sudhir的答案发表评论,我发表了我的评论作为答案。我使用了与Sudhir提出的解决方案基本相同的代码。模仿有效,但它有安全漏洞。如果将密码存储为纯文本(托管)字符串,则可以将其移动到内存中,甚至因为分页而存储到硬盘中。这使得非常规的人很容易窥探你的证件。
因此建议将SecureString用于此目的。可以在MSDN上查找如何将其与SecureString结合使用。与Sudhir解决方案的主要区别在于使用不同的LogonUser重载,即
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String username, String domain,
IntPtr password, int logonType,
int logonProvider, ref IntPtr token);
并像这样使用它(此代码来自MSDN):
// Marshal the SecureString to unmanaged memory.
passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(pwdSecureString);
// Call LogonUser, passing the unmanaged (and decrypted) copy of
// the SecureString password.
returnValue = LogonUser(userName, domainName, passwordPtr,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);
这样,密码只在我们用它来进行用户登录之前加密。之后,明文密码立即从内存中释放出来。