点击蓝色字关注“SQL数据库运维”,回复“SQL”获取2TB学习资源!
注意:这些实例可能位于同一服务器上,也可能位于不同的服务器上,并且它们的版本可能有所不同。
在本文中,服务器 A 和服务器 B 是不同的服务器。
在将一个数据库从服务器 A 上的 SQL Server 实例移动到服务器 B 上的 SQL Server 实例之后,用户可能无法登录到移至服务器 B 上的数据库。而且,用户可能会收到以下错误消息:
用户“MyUser”登录失败。(Microsoft SQL Server,错误:18456)
要传输登录名,请根据具体情况采用下列方法之一。
方法 1:重置目标 SQL Server 计算机(服务器 B)上的密码
要解决此问题,请在 SQL Server 计算机中重置密码,然后为登录名编写脚本。
注意
重置密码时,将使用密码哈希算法。
方法 2:使用在源服务器(服务器 A)上生成的脚本将登录名和密码传输到目标服务器(服务器 B)
创建存储过程有助于生成必要的脚本以传输登录名及其密码。为此,请使用 SQL Server Management Studio (SSMS) 或任何其他客户端工具连接到服务器 A,并运行以下脚本:
USE [master]GOIF OBJECT_ID ('sp_hexadecimal') IS NOT NULLDROP PROCEDURE sp_hexadecimalGOCREATE PROCEDURE [dbo].[sp_hexadecimal](@binvalue varbinary(256),@hexvalue varchar (514) OUTPUT)ASBEGINDECLARE @charvalue varchar (514)DECLARE @i intDECLARE @length intDECLARE @hexstring char(16)SELECT @charvalue = '0x'SELECT @i = 1SELECT @length = DATALENGTH (@binvalue)SELECT @hexstring = '0123456789ABCDEF'WHILE (@i <= @length)BEGINDECLARE @tempint intDECLARE @firstint intDECLARE @secondint intSELECT @tempint = CONVERT(int, SUBSTRING(@binvalue,@i,1))SELECT @firstint = FLOOR(@tempint/16)SELECT @secondint = @tempint - (@firstint*16)SELECT @charvalue = @charvalue + SUBSTRING(@hexstring, @firstint+1, 1) + SUBSTRING(@hexstring, @secondint+1, 1)SELECT @i = @i + 1ENDSELECT @hexvalue = @charvalueENDgoIF OBJECT_ID ('sp_help_revlogin') IS NOT NULLDROP PROCEDURE sp_help_revloginGOCREATE PROCEDURE [dbo].[sp_help_revlogin](@login_name sysname = NULL)ASBEGINDECLARE @name SYSNAMEDECLARE @type VARCHAR (1)DECLARE @hasaccess INTDECLARE @denylogin INTDECLARE @is_disabled INTDECLARE @PWD_varbinary VARBINARY (256)DECLARE @PWD_string VARCHAR (514)DECLARE @SID_varbinary VARBINARY (85)DECLARE @SID_string VARCHAR (514)DECLARE @tmpstr VARCHAR (1024)DECLARE @is_policy_checked VARCHAR (3)DECLARE @is_expiration_checked VARCHAR (3)Declare @Prefix VARCHAR(255)DECLARE @defaultdb SYSNAMEDECLARE @defaultlanguage SYSNAMEDECLARE @tmpstrRole VARCHAR (1024)IF (@login_name IS NULL)BEGINDECLARE login_curs CURSORFORSELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin, p.default_language_nameFROM sys.server_principals pLEFT JOIN sys.syslogins l ON ( l.name = p.name )WHERE p.type IN ( 'S', 'G', 'U' )AND p.name <> 'sa'ORDER BY p.nameENDELSEDECLARE login_curs CURSORFORSELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin, p.default_language_nameFROM sys.server_principals pLEFT JOIN sys.syslogins l ON ( l.name = p.name )WHERE p.type IN ( 'S', 'G', 'U' )AND p.name = @login_nameORDER BY p.nameOPEN login_cursFETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin, @defaultlanguageIF (@@fetch_status = -1)BEGINPRINT 'No login(s) found.'CLOSE login_cursDEALLOCATE login_cursRETURN -1ENDSET @tmpstr = '/* sp_help_revlogin script 'PRINT @tmpstrSET @tmpstr = '** Generated ' + CONVERT (varchar, GETDATE()) + ' on ' + @@SERVERNAME + ' */'PRINT @tmpstrPRINT ''WHILE (@@fetch_status <> -1)BEGINIF (@@fetch_status <> -2)BEGINPRINT ''SET @tmpstr = '-- Login: ' + @namePRINT @tmpstrSET @tmpstr='IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'''+@name+''')BEGIN'Print @tmpstrIF (@type IN ( 'G', 'U'))BEGIN -- NT authenticated account/groupSET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + @defaultdb + ']'ENDELSEBEGIN -- SQL Server authentication-- obtain password and sidSET @PWD_varbinary = CAST( LOGINPROPERTY( @name, 'PasswordHash' ) AS varbinary (256) )EXEC sp_hexadecimal @PWD_varbinary, @PWD_string OUTEXEC sp_hexadecimal @SID_varbinary,@SID_string OUT-- obtain password policy stateSELECT @is_policy_checked = CASE is_policy_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL ENDFROM sys.sql_loginsWHERE name = @nameSELECT @is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL ENDFROM sys.sql_loginsWHERE name = @nameSET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' WITH PASSWORD = ' + @PWD_string + ' HASHED, SID = '+ @SID_string + ', DEFAULT_DATABASE = [' + @defaultdb + ']' + ', DEFAULT_LANGUAGE = [' + @defaultlanguage + ']'IF ( @is_policy_checked IS NOT NULL )BEGINSET @tmpstr = @tmpstr + ', CHECK_POLICY = ' + @is_policy_checkedENDIF ( @is_expiration_checked IS NOT NULL )BEGINSET @tmpstr = @tmpstr + ', CHECK_EXPIRATION = ' + @is_expiration_checkedENDENDIF (@denylogin = 1)BEGIN -- login is denied accessSET @tmpstr = @tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( @name )ENDELSE IF (@hasaccess = 0)BEGIN -- login exists but does not have accessSET @tmpstr = @tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( @name )ENDIF (@is_disabled = 1)BEGIN -- login is disabledSET @tmpstr = @tmpstr + '; ALTER LOGIN ' + QUOTENAME( @name ) + ' DISABLE'ENDSET @Prefix = 'EXEC master.dbo.sp_addsrvrolemember @loginame='''SET @tmpstrRole=''SELECT @tmpstrRole = @tmpstrRole+ CASE WHEN sysadmin = 1 THEN @Prefix + [LoginName] + ''', @rolename=''sysadmin''' ELSE '' END+ CASE WHEN securityadmin = 1 THEN @Prefix + [LoginName] + ''', @rolename=''securityadmin''' ELSE '' END+ CASE WHEN serveradmin = 1 THEN @Prefix + [LoginName] + ''', @rolename=''serveradmin''' ELSE '' END+ CASE WHEN setupadmin = 1 THEN @Prefix + [LoginName] + ''', @rolename=''setupadmin''' ELSE '' END+ CASE WHEN processadmin = 1 THEN @Prefix + [LoginName] + ''', @rolename=''processadmin''' ELSE '' END+ CASE WHEN diskadmin = 1 THEN @Prefix + [LoginName] + ''', @rolename=''diskadmin''' ELSE '' END+ CASE WHEN dbcreator = 1 THEN @Prefix + [LoginName] + ''', @rolename=''dbcreator''' ELSE '' END+ CASE WHEN bulkadmin = 1 THEN @Prefix + [LoginName] + ''', @rolename=''bulkadmin''' ELSE '' ENDFROM (SELECT CONVERT(VARCHAR(100),SUSER_SNAME(sid)) AS [LoginName],sysadmin,securityadmin,serveradmin,setupadmin,processadmin,diskadmin,dbcreator,bulkadminFROM sys.sysloginsWHERE ( sysadmin<>0OR securityadmin<>0OR serveradmin<>0OR setupadmin <>0OR processadmin <>0OR diskadmin<>0OR dbcreator<>0OR bulkadmin<>0)AND name=@name) LPRINT @tmpstrPRINT @tmpstrRolePRINT 'END'ENDFETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin, @defaultlanguageENDCLOSE login_cursDEALLOCATE login_cursRETURN 0END
注意
此脚本会在“master”数据库中创建两个存储过程。过程分别名为“sp_hexadecimal”和“sp_help_revlogin”。
EXEC sp_help_revlogin

目标服务器(服务器B)上的步骤
使用任何客户端工具(如 SSMS)连接到服务器 B,然后运行步骤 4 中服务器 A 生成的脚本(sp_helprevlogin的输出)。
备注
在服务器B上的实例上运行输出脚本之前,请查看以下信息:
可以通过以下方式对密码进行哈希处理:
VERSION_SHA1:此哈希是使用SHA1算法生成的,并在SQL Server 2000到SQL Server 2008 R2中使用。
VERSION_SHA2:此哈希是使用SHA2 512算法生成的,用于SQL Server 2012及更高版本。
仔细查看输出脚本。如果服务器A和服务器B位于不同的域中,则必须更改输出脚本。然后,您必须使用CREATE LOGIN语句中的新域名替换原始域名。在新域中授予访问权限的集成登录名与原始域中的登录名不同。因此,用户会从这些登录名中孤立出来。有关如何解决这些孤立用户的更多信息,请复制以下提供的官网链接,以查看Microsoft知识库中相应的文章:当在运行 SQL Server 的服务器之间移动数据库时,如何解决权限问题;https://learn.microsoft.com/zh-CN/sql/t-sql/statements/alter-user-transact-sql?view=sql-server-ver15
如果服务器A和服务器B位于同一域中,则使用相同的SID。因此,用户不太可能成为孤儿。
在输出脚本中,使用加密密码创建登录。这是因为CREATE LOGIN语句中的HASHED参数。此参数指定在PASSWORD参数之后输入的密码已经过哈希处理。
默认情况下,只有sysadmin固定服务器角色的成员才能从sys.server_principals视图运行SELECT语句。除非sysadmin固定服务器角色的成员向用户授予必要的权限,否则用户无法创建或运行输出脚本。
本文中的步骤不会传输特定登录的默认数据库信息。这是因为服务器B上的默认数据库可能并不总是存在。要为登录定义默认数据库,请使用ALTER LOGIN语句,将登录名和默认数据库作为参数传入。
对源服务器和目标服务器排序:
不区分大小写的服务器A和区分大小写的服务器B:服务器A的排序顺序可能不区分大小写,服务器B的排序顺序可能区分大小写。在这种情况下,用户必须在将登录名和密码传输到服务器B上的实例后,以全大写字母键入密码。
区分大小写的服务器A和不区分大小写的服务器B:服务器A的排序顺序可能区分大小写,并且服务器B的排序顺序可能不区分大小写。在这种情况下,除非满足下列条件之一,否则用户无法使用您在服务器B上传输到实例的登录名和密码登录:
原始密码不包含字母。
原始密码中的所有字母均为大写字母。
两个服务器上区分大小写或不区分大小写:服务器A和服务器B的排序顺序可能区分大小写,或者服务器A和服务器B的排序顺序可能不区分大小写。在这些情况下,用户不会遇到问题。
已经在服务器B上的实例中的登录名可以具有与输出脚本中的名称相同的名称。在这种情况下,当您在服务器B上的实例上运行输出脚本时,您会收到以下错误消息:
消息15025,级别16,状态1,行1
服务器主体“ MyLogin ”已存在。类似地,已经在服务器B上的实例中的登录可以具有与输出脚本中的SID相同的SID。在这种情况下,当您在服务器B上的实例上运行输出脚本时,您会收到以下错误消息:
消息15433,级别16,状态1,行1
提供的参数sid正在使用中。因此,您必须执行以下操作:
仔细查看输出脚本。
检查服务器B上实例中sys.server_principals视图的内容。
适当地解决这些错误消息。
在SQL Server 2005中,登录的SID用于实现数据库级访问。登录可以在服务器上的不同数据库中具有不同的SID。在这种情况下,登录只能访问具有与sys.server_principals视图中的SID匹配的SID的数据库。如果两个数据库从不同的服务器组合,则可能会出现此问题。若要解决此问题,请使用DROP USER语句从具有SID不匹配的数据库中手动删除登录。然后,使用CREATE USER语句再次添加登录。如果您尝试使用脚本化的SQL Server 2000之前的登录名创建新的SQL Server 2012登录,则会收到以下错误消息:
参数PASSWORD的值无效。指定有效的参数值。
注意您在SQL Server 2012中收到此错误,因为为CREATE LOGIN和ALTER LOGIN语句提供了16字节的密码哈希。
若要在运行SQL Server 2012的服务器上解决此问题,请创建一个密码为空的登录名。为此,请运行以下脚本:
CREATE LOGIN [Test] WITH PASSWORD = '', SID = 0x90FD605DCEFAE14FAB4D5EB0BBA1AECC, DEFAULT_DATABASE = [master], CHECK_POLICY = ON, CHECK_EXPIRATION = OFF
创建具有空密码的登录名后,用户可以在下次登录尝试时更改密码。
方法3:使用SQL Server 2000之前的密码登录
注意仅当您将SQL Server 2000迁移到更新的受支持版本的SQL Server时,此方法才适用。
在这种情况下,请求用户使用SQL Server 2000之前的登录名登录到运行SQL Server的服务器。
注意当用户使用SQL Server 2000之前的密码登录时,密码哈希会自动更新。
使用T-SQL获取登录用户的T-SQL(推荐)
SELECT 'CREATE LOGIN [' + p.name + '] '+ CASE WHEN p.type IN ( 'U', 'G' ) THEN 'FROM windows 'ELSE ''END + 'WITH ' + CASE WHEN p.type = 'S'THEN 'password = '+ master.sys.fn_varbintohexstr(l.password_hash)+ ' hashed, ' + 'sid = '+ master.sys.fn_varbintohexstr(l.sid)+ ', check_expiration = '+ CASE WHEN l.is_expiration_checked > 0THEN 'ON, 'ELSE 'OFF, 'END + 'check_policy = '+ CASE WHEN l.is_policy_checked > 0THEN 'ON, 'ELSE 'OFF, 'END+ CASE WHEN l.credential_id > 0THEN 'credential = ' + c.name+ ', 'ELSE ''ENDELSE ''END + 'default_database = '+ p.default_database_name+ CASE WHEN LEN(p.default_language_name) > 0THEN ', default_language = ' + p.default_language_nameELSE ''ENDFROM sys.server_principals pLEFT JOIN sys.sql_logins lON p.principal_id = l.principal_idLEFT JOIN sys.credentials cON l.credential_id = c.credential_idWHERE p.type IN ( 'S', 'U', 'G' )--AND p.name NOT IN ( 'sa')AND p.name NOT LIKE '%##%'AND p.name NOT LIKE '%NT SERVICE%'AND p.name NOT LIKE '%NT AUTHORITY%'

文章参考资料:https://www.cnblogs.com/gered/p/11511365.html
Microsoft官网:https://learn.microsoft.com/zh-CN/troubleshoot/sql/security/transfer-logins-passwords-between-instances

点击关注“SQL数据库运维”,后台或浏览至公众号文章底部点击“发消息”回复关键字:进群,带你进入高手如云的技术交流群。后台回复关键字:SQL,获取学习资料。
动动小手点击加关注呦☟☟☟




