When performing database migration, one of the main tasks is to perform login name migration.
User names can basically be migrated during database backup and restore, and the mapping relationship and permissions remain unchanged before and after migration.
Login names, etc. can be migrated during the backup and restore of the master database, but because the master is used as a system library, it stores a lot of system information of the original server. After the master library is restored, the original server information will be migrated synchronously, which will cause problems with the existing server information. Conflicts, a lot of extra work needs to be done to remove the impact.
At this time, if you can use a script to migrate the login name, it will greatly facilitate our work.
There are already powerful gods on the Internet who have shared the stored procedures for migrating login names and passwords early. The stored procedure script will be attached at the end of the article. I don't know which great god wrote it first, and I can't find the original article.
However, this stored procedure can only migrate login names and passwords, and server roles cannot be migrated. Therefore, after migrating login names, server roles, users and permissions must be migrated together. Many examples on the Internet are not uniformly explained.
Use the following script to view the server roles of a login, and migrate the server roles together after the login is migrated.
--查看登录名服务器角色
SELECT sp.name AS [login_name]
,CASE WHEN sp.[type]='S' THEN 'SQL 登录名'
WHEN sp.[type]='U' THEN 'Windows 登录名'
WHEN sp.[type]='G' THEN 'Windows 组'
WHEN sp.[type]='R' THEN '服务器角色'
WHEN sp.[type]='C' THEN '映射到证书的登录名'
WHEN sp.[type]='K' THEN '映射到非对称密钥的登录名'
END AS [principal_type]
,sp.is_disabled
,ISNULL(sp.default_database_name,'') as [default_database_name]
,ISNULL(rsp.name,'') AS [server_role]
,STUFF((SELECT ','+permission_name FROM sys.server_permissions spp where sp.principal_id=spp.grantee_principal_id for xml path('')),1,1,'') as [permissions]
FROM sys.server_principals sp
LEFT JOIN sys.server_role_members srm ON sp.principal_id=srm.member_principal_id
LEFT JOIN sys.server_principals rsp ON srm.role_principal_id=rsp.principal_id
where rsp.name is not null
ORDER BY [principal_type],sp.principal_id
--授权服务器角色
select N'EXEC sp_addsrvrolemember N''' +sp.name+ ''' ,N''' + rsp.name+''' '
FROM sys.server_principals sp
LEFT JOIN sys.server_role_members srm ON sp.principal_id=srm.member_principal_id
LEFT JOIN sys.server_principals rsp ON srm.role_principal_id=rsp.principal_id
where rsp.name is not null
-- 将登录名添加为某个服务器级角色的成员
EXEC sp_addsrvrolemember @loginame= 'kk' ,@rolename = 'sysadmin'
master库中的用户名及权限可以用如下脚本进行迁移。
-- 授予【数据库角色成员身份】权限
SELECT 'exec sp_addrolemember N'''+g.name+''', N'''+u.name+''''
FROM sys.database_principals u
inner join sys.database_role_members m on u.principal_id = m.member_principal_id
inner join sys.database_principals g on g.principal_id = m.role_principal_id
ORDER BY g.name,u.name
-- 授予【安全对象】权限
SELECT N'grant '+B.permission_name collate chinese_prc_ci_ai_ws+N' on ['+A.name+N'] to ['+C.name+N']'
FROM sys.sysobjects A(NOLOCK)
INNER JOIN sys.database_permissions B(NOLOCK) ON A.id=B.major_id
INNER JOIN sys.database_principals C(NOLOCK) ON B.grantee_principal_id=C.principal_id
--WHERE C.name='kk' --A.name='objectName'
After all database migrations are completed, there should be an operation to check the database user name, that is, to find out whether there are orphaned users in the database. Orphaned user, as the name implies, the user is orphaned, and there is no mapping relationship with the login name. There are generally two reasons for this. First: the user does not have a corresponding login name, and the login name is lost. Second: in the database migration process, the database is migrated first, and then the login name is migrated. At this time, the mapping relationship between the login name and the user is lost.
Orphaned users cannot use the database. Even if the corresponding login name exists, it cannot be used without the mapping relationship.
This problem can be solved with the following script:
--查看当前数据库中是否存在孤立用户(只有用户,没有对应的登录名)
exec sp_change_users_login @action='Report'
-- 对孤立用户连接到现有的登录名
use mytest;
exec sp_change_users_login
@action='update_one',
@usernamepattern='kk', --数据库孤立用户
@loginname='kk'; --关联到sql server登录名
go
Next, attach the stored procedure script:
EXEC sp_help_revlogin --获取生成登录名的创建脚本
--存储过程脚本:
USE master
GO
IF OBJECT_ID ('sp_hexadecimal') IS NOT NULL
DROP PROCEDURE sp_hexadecimal
GO
CREATE PROCEDURE sp_hexadecimal
@binvalue varbinary(256),
@hexvalue varchar (514) OUTPUT
AS
DECLARE @charvalue varchar (514)
DECLARE @i int
DECLARE @length int
DECLARE @hexstring char(16)
SELECT @charvalue = '0x'
SELECT @i = 1
SELECT @length = DATALENGTH (@binvalue)
SELECT @hexstring = '0123456789ABCDEF'
WHILE (@i <= @length)
BEGIN
DECLARE @tempint int
DECLARE @firstint int
DECLARE @secondint int
SELECT @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 + 1
END
SELECT @hexvalue = @charvalue
GO
IF OBJECT_ID ('sp_help_revlogin') IS NOT NULL
DROP PROCEDURE sp_help_revlogin
GO
CREATE PROCEDURE sp_help_revlogin @login_name sysname = NULL AS
DECLARE @name sysname
DECLARE @type varchar (1)
DECLARE @hasaccess int
DECLARE @denylogin int
DECLARE @is_disabled int
DECLARE @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 @defaultdb sysname
IF (@login_name IS NULL)
DECLARE login_curs CURSOR FOR
SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
sys.server_principals p LEFT JOIN sys.syslogins l
ON ( l.name = p.name ) WHERE p.type IN ( 'S', 'G', 'U' ) AND p.name <> 'sa'
ELSE
DECLARE login_curs CURSOR FOR
SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
sys.server_principals p LEFT JOIN sys.syslogins l
ON ( l.name = p.name ) WHERE p.type IN ( 'S', 'G', 'U' ) AND p.name = @login_name
OPEN login_curs
FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
IF (@@fetch_status = -1)
BEGIN
PRINT 'No login(s) found.'
CLOSE login_curs
DEALLOCATE login_curs
RETURN -1
END
SET @tmpstr = '/* sp_help_revlogin script '
PRINT @tmpstr
SET @tmpstr = '** Generated ' + CONVERT (varchar, GETDATE()) + ' on ' + @@SERVERNAME + ' */'
PRINT @tmpstr
PRINT ''
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN
PRINT ''
SET @tmpstr = '-- Login: ' + @name
PRINT @tmpstr
IF (@type IN ( 'G', 'U'))
BEGIN -- NT authenticated account/group
SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + @defaultdb + ']'
END
ELSE BEGIN -- SQL Server authentication
-- obtain password and sid
SET @PWD_varbinary = CAST( LOGINPROPERTY( @name, 'PasswordHash' ) AS varbinary (256) )
EXEC sp_hexadecimal @PWD_varbinary, @PWD_string OUT
EXEC sp_hexadecimal @SID_varbinary,@SID_string OUT
-- obtain password policy state
SELECT @is_policy_checked = CASE is_policy_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = @name
SELECT @is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = @name
SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' WITH PASSWORD = ' + @PWD_string + ' HASHED, SID = ' + @SID_string + ', DEFAULT_DATABASE = [' + @defaultdb + ']'
IF ( @is_policy_checked IS NOT NULL )
BEGIN
SET @tmpstr = @tmpstr + ', CHECK_POLICY = ' + @is_policy_checked
END
IF ( @is_expiration_checked IS NOT NULL )
BEGIN
SET @tmpstr = @tmpstr + ', CHECK_EXPIRATION = ' + @is_expiration_checked
END
END
IF (@denylogin = 1)
BEGIN -- login is denied access
SET @tmpstr = @tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( @name )
END
ELSE IF (@hasaccess = 0)
BEGIN -- login exists but does not have access
SET @tmpstr = @tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( @name )
END
IF (@is_disabled = 1)
BEGIN -- login is disabled
SET @tmpstr = @tmpstr + '; ALTER LOGIN ' + QUOTENAME( @name ) + ' DISABLE'
END
PRINT @tmpstr
END
FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
END
CLOSE login_curs
DEALLOCATE login_curs
RETURN 0
GO