1

我陷入了一个非常奇怪的境地。

我有一个工作流,用于在我的 Web 应用程序上配置新站点。此工作流使用一个自定义工作流活动来使用以下语句配置站点。

---为清楚起见省略了其他代码---

SPSiteCollection.Add()

当我的应用程序池帐户与 Central Admin 应用程序池帐户不同时,此语句会引发跟随异常。

访问被拒绝。(来自 HRESULT 的异常:0x80070005 (E_ACCESSDENIED))在 Microsoft.SharePoint.SPGlobal.HandleUnauthorizedAccessException(UnauthorizedAccessException ex) 在 Microsoft.SharePoint.Library.SPRequest.CreateSite(Guid gApplicationId, String bstrUrl, Int32 lZone, Guid gSiteId, Guid gDatabaseId, String bstrDat

经过大量谷歌搜索和发现后,我已将 Applicaiton Pool 帐户权限归零。

工作流代码始终在系统帐户(应用程序池标识)下运行。为了创建新的 SharePoint 网站集,应用程序池需要访问“SharePoint_Config”数据库。

当我的 Web 应用程序在 Central Admin 的应用程序池凭据下运行时,它具有对配置数据库的所有访问权限。但是,当我在任何其他权限较少的应用程序池身份下运行时。即使我在配置数据库中授予应用程序池帐户的 DBO 权限,它也会引发异常。

我的应用程序事件日志有以下条目:-

事件源:Windows SharePoint Services 3 事件类别:数据库事件 ID:3760 日期:2010 年 2 月 3 日时间:上午 2:36:16 用户:N/A 计算机:SHAREPOINT20 描述:SQL Server 实例上的 SQL 数据库“SharePoint_Config” houspsr001' 未找到。下面包括来自 SQL Server 的其他错误信息。

无法打开登录请求的数据库“SharePoint_Config”。登录失败。用户“DOMAIN\WebAppPool”登录失败。

有关详细信息,请参阅http://go.microsoft.com/fwlink/events.asp上的帮助和支持中心 。

我的问题是......在中央管理员的应用程序池帐户下运行此类代码是否是强制性的。

任何解决方法......?

我的问题

4

2 回答 2

1

终于解决了访问被拒绝的问题。正如我在上一封电子邮件中提到的那样,问题是由于对我的应用程序池身份的权限不足。

  • Central Admin 在不同的应用程序池标识下运行
  • Web 应用程序在不同的应用程序池标识下运行。

我的工作流使用 ElevatedPrevilages 来配置网站集,并且它曾经从数据库中获取拒绝访问,因为它没有修改 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();
于 2010-02-09T13:48:18.723 回答
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);

这样,密码仅在我们使用它进行用户登录之前才被加密。之后,明文密码立即从内存中释放出来。

于 2010-12-20T14:34:18.100 回答