SqlConnection 使用 Dynamics GP 用户名和密码——用户登录失败

Posted

技术标签:

【中文标题】SqlConnection 使用 Dynamics GP 用户名和密码——用户登录失败【英文标题】:SqlConnection using Dynamics GP username and password -- Login failed for user 【发布时间】:2018-07-17 09:36:57 【问题描述】:

我正在为 Dynamics GP 编写加载项,并且正在尝试运行自定义存储过程,但是当我尝试打开连接时,我收到一条错误消息,提示“用户登录失败:”。

如果我手动使用系统管理员用户名和密码,一切都会正确执行。但是,我想使用登录 Dynamics GP 的人的用户名和密码。

string gpUsername = Dynamics.Globals.UserId.Value;
string gpPassword = Dynamics.Globals.SqlPassword.Value;
string companyDb = Dynamics.Globals.IntercompanyId.Value;
string dataSource = "sql-server-name";

SqlConnectionStringBuilder constringBuilder = new SqlConnectionStringBuilder();
constringBuilder.UserID = gpUsername;
constringBuilder.Password = gpPassword;
constringBuilder.InitialCatalog = companyDb;
constringBuilder.DataSource = dataSource;
constringBuilder.WorkstationID = Environment.MachineName;
constringBuilder.MultipleActiveResultSets = true;
constringBuilder.ApplicationName = $"FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductName (GP)";
constringBuilder.IntegratedSecurity = false;
string constring = constringBuilder.ConnectionString;
SqlConnection con = new SqlConnection(constring);
con.Open();

这是完整的堆栈跟踪:

Dynamics.exe Warning: 0 : Exception occurred while updating Item class 1255. Review the error message below.
    System.Data.SqlClient.SqlException (0x80131904): Login failed for user '<username>'.
   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, DbConnectionPool pool, String accessToken, Boolean applyTransientFaultHandling, SqlAuthenticationProviderManager sqlAuthProviderManager)
   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at Dyn2Bib.GPAddIn.ProcessTransaction(String srs, String srsName, String changeType)
ClientConnectionId:1a4c93c1-0efd-4590-8c9f-98265eaafaa2
Error Number:18456,State:1,Class:14

如果我把constringBuilder中的DataSource改成Dynamics.Globals.SqlDataSourceName.Value,就好像在网络上根本找不到数据源,报错:

Dynamics.exe Warning: 0 : Exception occurred while updating Item class 1255. Review the error message below.
    System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) ---> System.ComponentModel.Win32Exception (0x80004005): The network path was not found
   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, DbConnectionPool pool, String accessToken, Boolean applyTransientFaultHandling, SqlAuthenticationProviderManager sqlAuthProviderManager)
   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at Dyn2Bib.GPAddIn.ProcessTransaction(String srs, String srsName, String changeType)
ClientConnectionId:00000000-0000-0000-0000-000000000000
Error Number:53,State:0,Class:20

如果我将连接字符串打印到屏幕上,它看起来是正确的,正如我所提到的,如果我手动使用系统管理员帐户,它就可以工作。

我是否能够使用 Dynamics GP 帐户直接连接到 SQL 服务器?服务器上有相应的登录名,所以看起来应该已经正确配置了。有谁知道我在这里做错了什么?我是否需要对 Dynamics GP 帐户进行任何更改以允许它使用 SQL 身份验证进行连接?

【问题讨论】:

尝试使用该帐户登录到 sql 直接(例如您自己使用 sql manager),并检查是否有使用相同密码的该名称的 SQL 登录 如果我从 SQL Server 管理连接窗口使用 SQL Server 身份验证,则无法使用 GP 帐户登录。但是,服务器的登录确实存在,并且能够登录到 Dynamics GP。 我发现的唯一一件事(我确定你也用谷歌搜索过)是服务器名称必须 100% 完全相同,因为你管理用户连接的位置,例如 server01 与Server01 或 server01.domain 等 是的,是一样的 【参考方案1】:

方法是使用GP中的连接对象

using Dex = Microsoft.Dexterity.Applications;

        internal static SqlConnection conn = new SqlConnection();

        internal static SqlConnection GetOpenGPConnection()
        
            try
            
		//Call Startup
		int resp = GPConnection.Startup();

		// Create the connection object
		Microsoft.Dexterity.GPConnection GPConn = new GPConnection();

		//  Initialize
		GPConn.Init("MyVSTDCompanyid", "myVSTDGPAuthCode");  // get this from Microsoft GP Dev Tools Support
		conn.ConnectionString = String.Format("Database = 0", Dex.Dynamics.Globals.IntercompanyId.Value);

		GPConn.Connect(conn, Dex.Dynamics.Globals.SqlDataSourceName, Dex.Dynamics.Globals.UserId, Dex.Dynamics.Globals.SqlPassword);

		if ((GPConn.ReturnCode & (int)GPConnection.ReturnCodeFlags.SuccessfulLogin) == (int)GPConnection.ReturnCodeFlags.SuccessfulLogin)
		
		return conn;
		
		else
		//...  user authenticaltion failed - backup plan?  fallback to windows auth?  
		return null
		
	
            
            catch (Exception ex)
            
            	// your catch handling mechanism
            
        

【讨论】:

Microsoft.Dexterity.Applications 不存在。完成这项工作的组件在哪里? Visual Studio Tools for Dynamics GP Applications.Dynamics.dll 位于 GP 安装目录(例如 C:\Program Files (x86)\Microsoft Dynamics\GP2018)以及 GP2018 VS Tools SDK 目录中。这是包含 Microsoft.Dexterity.Applications 命名空间的程序集。【参考方案2】:

我假设登录到 Dynamics GP 的用户的用户名和密码只是 Dynamics GP 应用程序用户,而不是实际的数据库用户。

您可以通过将身份验证更改为 Windows 身份验证来解决此问题

实现如下:

    创建一个窗口组 使用应该能够运行您的自定义 SP 的每个 Windows 用户填充此 Windows 组 使用 SQL Server Management Studio,将此 windows 组添加为 SQL Server 登录名 在您的代码中,

删除这些行:

constringBuilder.UserID = gpUsername;
constringBuilder.Password = gpPassword;

并像这样改变这一行:

 constringBuilder.IntegratedSecurity = true;

这意味着将使用他们的 Windows 凭据建立连接,而不是使用(无效的)SQL 凭据

这些 Windows 凭据之前已通过完成步骤 1-3 获得服务器登录访问权限

这不是故事的结局。现在您需要让群组有权访问您的 SP

    使用存储过程在数据库中创建数据库角色 授予此角色对您的自定义 SP 的执行权限 最后,要将其全部链接起来,请编辑您在第 3 步中创建的登录名,授予其访问数据库的权限并将其添加到您在第 5 步中创建的角色中

复杂吗?是的。但是授予用户访问数据库的权限是一件大事。这边

用户可以登录并运行 SP,不能进行其他操作。 要授予和删除用户,您可以在 windows 组中添加或删除他们(如果您愿意,可以重复使用预先存在的 GP windows 组) 如果您创建了他们需要访问的其他数据库对象,则您授予数据库角色访问这些对象的权限

如果此解决方案对您有吸引力,如果您愿意,我可以对其进行扩展。

【讨论】:

Windows 身份验证实际上是我最初的计划,但由于它需要维护一个额外的安全组,我想看看是否可以使用 Dynamics GP 用户的凭据,这应该是因为它们是使用数据库登录名/用户设置的。此外,它不考虑某人何时可能出于任何原因在其他人的计算机上登录 GP。 如果您确定 Dynamics GP 用户也是数据库用户,我建议您实际上尝试使用一些 GP 凭据使用 SSMS 直接登录。我猜测用于 SQL 用户的密码是由 GP 应用程序以某种方式转换的。您还可以使用 SQL Server 日志来明确识别登录是否正确但密码是否正确。 关于附加安全组,通常已经存在一个。关于 windows 和 GP 用户不同,您可以通过多种方式将 GP 用户保存在连接字符串中:***.com/questions/25581002/…【参考方案3】:

GP 使用 SQL 身份验证。 GP 用户是 SQL 登录名。 (您可以使用 FastPath 将 Windows Auth 与 GP 一起使用。越来越流行,MS 也一直在谈论对它的原生支持)

虽然用户有 SQL 登录名,但应用程序会在 SQL 上创建帐户并对密码进行哈希处理。这是为了防止用户使用水晶之类的东西来查询他们无权访问的数据

【讨论】:

感谢您的评论,但您并未提出解决方案。

以上是关于SqlConnection 使用 Dynamics GP 用户名和密码——用户登录失败的主要内容,如果未能解决你的问题,请参考以下文章

C# CancellationToken 如何与 SqlConnection.OpenAsync(token) 一起使用?

using (SqlConnection connection = new SqlConnection(connectionString))

使用 SQLConnection 编译错误

sqlConnection/Command 使用语句 + try/catch 块 [重复]

using(SqlConnection connection=new SqlConnection(connectionString))

如何判断SqlConnection是否附加了SqlDataReader?