fix orphaned user

orphan user是某个数据库的user,只有user name而没有login,即,在存在于sys.database_principals 中, 而不存在于 sys.server_principals 中。一般情况下,将备份的数据库在其它Server上还原时,会产生orphan user。

一,查看Orphaned Users

Login 和 User的Mapping 关系是通过SID(security ID)来关联的,如果一个SID 存在于sys.database_principals,而不存在于  sys.server_principals,那么这个User 除非是system user,否则就是orphaned user.

1,使用以下脚本查看Orphaned Users

select dp.name as UserName,dp.type,dp.type_desc,dp.default_schema_name,dp.is_fixed_role,
    dp.authentication_type,dp.authentication_type_desc,dp.sid,dp.principal_id
from sys.database_principals dp
left join sys.server_principals sp 
on dp.sid=sp.sid
where sp.sid is null
and dp.[type] IN (N'U', N'S',N'G')
AND dp.is_fixed_role = 0
AND dp.[Name] NOT IN (N'dbo', N'guest', N'sys', N'INFORMATION_SCHEMA')

sys.database_principals Principal type:

  • S = SQL user
  • U = Windows user
  • G = Windows group
  • R = Database role

sys.server_principals Principal type:

  • S = SQL login
  • U = Windows login
  • G = Windows group
  • R = Server role

2,创建SQL Login, Windows Login 和 Windows Group

既然是User没有对应的Login,那么就需要创建Login,在创建Login时,需要指定login name,SQL Login Name 和Windows Login Name 的格式不相同的。

2.1,创建Windows Login和 Windows Group的语法相同

When you are creating logins that are mapped from a Windows domain account, you must use the pre-Windows 2000 user logon name in the format [<domainName>\<login_name>].

CREATE LOGIN [DomainName\WindowsLoginName] -- or [DomainName\WindowsGroupName] 
FROM WINDOWS WITH DEFAULT_DATABASE=[master], 
DEFAULT_LANGUAGE=[us_english]

2.2,创建SQL Login

SQL Server authentication logins are type sysname and must conform to the rules for Identifiers and cannot contain a '\'. Windows logins can contain a '\'.

CREATE LOGIN [SQLLoginName] 
WITH PASSWORD=N'faLwQqnC8vD+eP+Ft13/pAXrJ2b7LlBM9BNEwnzIYDo=', 
DEFAULT_DATABASE=[master], 
DEFAULT_LANGUAGE=[us_english], 
CHECK_EXPIRATION=ON, 
CHECK_POLICY=ON


三,Create user

Users based on logins in master This is the most common type of user.

  • User based on a login based on a Windows user.

  • User based on a login based on a Windows group.

  • User based on a login using SQL Server authentication.

--Users based on logins in master
CREATE USER user_name 
    [ 
        { FOR | FROM } LOGIN login_name 
    ]
    [ WITH DEFAULT_SCHEMA = schema_name ] 
[ ; ]

user_name  

Specifies the name by which the user is identified inside this database. user_name is a sysname. It can be up to 128 characters long.

login_name  

Can be a login based on a Windows principal (user or group), or a login using SQL Server authentication. When this SQL Server login enters the database, it acquires the name and ID of the database user that is being created. When creating a login mapped from a Windows principal, use the format [<domainName>\<loginName>].

Create User 是创建User和Login之间的Mapping 关系,上文提到,这种Mapping 关系是通过SID来关联的,即Login访问数据库使用的User的SID和Login相同。

For Example

CREATE USER  [UserName]
FOR LOGIN [Domain\WindowsLoginName] --or [SQLLoginname]
WITH DEFAULT_SCHEMA=[dbo]
GO

从语法上看,当创建User时,UserName  和 LoginName 之间没有关系。如果使用Windows Login,那么UserName可以是LoginName,也可以是不是。

四,自动修复Orphaned users 问题

由于UserName  和 LoginName 之间没有关系,完全修复的可能性没有。但是,如果使用Windows验证,假设UserName 和 LoginName相同是可以的,那么可以使用Create Login from windows 创建Windows Login。

1,使用Alter User with login 重新创建Login 和 name之间的mapping 关系,Login 和 User的Mapping 关系是通过SID(security ID)来关联的。 

ALTER USER userName  
     WITH <set_item> [ ,...n ]

<set_item> ::= 
      NAME = newUserName 
    | DEFAULT_SCHEMA = { schemaName | NULL }
    | LOGIN = loginName
    | PASSWORD = 'password' [ OLD_PASSWORD = 'oldpassword' ]
    | DEFAULT_LANGUAGE = { NONE | <lcid> | <language name> | <language alias> }

LOGIN =loginName              

Re-maps a user to another login by changing the user's Security Identifier (SID) to match the login's SID.

Remarks

You can change the name of a user who is mapped to a Windows login or group only when the SID of the new user name matches the SID that is recorded in the database. This check helps prevent spoofing of Windows logins in the database.

The WITH LOGIN clause enables the remapping of a user to a different login. Only SQL users and Windows users (or groups) can be remapped. The WITH LOGIN clause cannot be used to change the type of user, such as changing a Windows account to a SQL Server login.

The name of the user will be automatically renamed to the login name if the following conditions are true.

  • The user is a Windows user.

  • The name is a Windows name (contains a backslash).

  • No new name was specified.

  • The current name differs from the login name.

Otherwise, the user will not be renamed unless the caller additionally invokes the NAME clause.

The name of a user mapped to a SQL Server login, a certificate, or an asymmetric key cannot contain the backslash character (\).

2,在重建login 和 user之间的mapping 关系时,user name 在满足一定条件时,能够被重命名为login name.

CREATE USER  [username_xxx]
FOR LOGIN [domain\xxxx]
WITH DEFAULT_SCHEMA=[dbo]
GO

--user name [username_xxx] 被重命名为 [domain\yyyy]
alter user [username_xxx]
with login= [domain\yyyy]
, DEFAULT_SCHEMA=[dbo]
GO

3,自动修复Orphaned Users的script

DECLARE @username sysname
DECLARE @sqlcmd NVARCHAR(max) = N''
 
IF OBJECT_ID(N'tempdb..#OrphanedUsers') IS NOT NULL 
DROP TABLE #OrphanedUsers

CREATE TABLE #OrphanedUsers 
(
    UserName sysname
)

INSERT INTO #OrphanedUsers (UserName)
SELECT [name]
FROM sys.database_principals
WHERE [type] IN (N'U', N'S')
AND is_fixed_role = 0
AND [Name] NOT IN (N'dbo', N'guest', N'sys', N'INFORMATION_SCHEMA');


DECLARE cur_orphaned CURSOR 
LOCAL
FORWARD_ONLY
FAST_FORWARD
READ_ONLY
for 
SELECT UserName
FROM #OrphanedUsers

open cur_orphaned

FETCH next FROM cur_orphaned into @username

while @@fetch_status=0
BEGIN

    IF not exists (SELECT null FROM sys.server_principals WHERE [Name] = @username)
    BEGIN
        IF EXISTS(SELECT 1 FROM sys.database_principals WHERE [Name] = @username AND type_desc = N'WINDOWS_USER')
        BEGIN
            SET @sqlcmd = N'CREATE LOGIN [' + @username + N'] FROM WINDOWS'
            Exec(@sqlcmd)
        END
        ELSE -- type_desc = N'SQL_USER'
        BEGIN
            SET @sqlcmd = N'CREATE LOGIN [' + @username + N'] WITH PASSWORD = N''password'''
            Exec(@sqlcmd)
        END
    END

    SET @sqlcmd = N'ALTER USER [' + @username + N'] WITH LOGIN = [' + @username + N']'
    Exec(@sqlcmd)

    FETCH next FROM cur_orphaned into @username
END

CLOSE cur_orphaned
DEALLOCATE cur_orphaned

五,guest

虽然术语“登录”和“用户”经常交换使用,但它们之间有很大的不同。登录用于用户身份验证,而数据库用户帐户用于数据库访问和权限验证。登录通过安全识别符 (SID) 与用户关联。访问 SQL Server 服务器需要登录。验证特定登录是否有效的过程称为“身份验证”。登录必须与 SQL Server 数据库用户相关联。您使用用户帐户控制数据库中执行的活动。如果数据库中不存在针对特定登录的用户帐户,使用该登录的用户即使能够连接到 SQL Server 服务器,也无法访问数据库。但是,该情形的唯一例外是当数据库包含“guest”用户帐户时。与用户帐户不关联的登录将被映射到 guest 用户。相反,如果存在数据库用户,但没有与其关联的登录,则该用户将无法登录到 SQL Server 服务器中。

 

参考文档:

Fixing orphaned database users in 2005 to 2012 – T-SQL Tuesday #025

Do you still use sp_change_users_login instead of ALTER USER UserName WITH LOGIN = UserName

 

你可能感兴趣的:(fix orphaned user)