Cloud SQL for SQL Server 迁移实战:用 sp_help_revlogin 保留登录名与 SID

2026-09-10 39 预计阅读时间: 1 分钟
来源: cloud.google.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

使用 Google Cloud Database Migration Service(DMS)完成数据复制后,真正的切换工作并不一定结束。数据库架构和事务数据已经同步,应用却可能在连接 Cloud SQL for SQL Server 时收到 Msg 18456: Login failed for user

常见原因是:DMS 迁移了数据库级对象和数据,但没有迁移源 SQL Server 实例级的登录名、密码哈希以及权限。解决问题的关键,不是重新猜测密码或批量手工建用户,而是让目标实例中的登录名拥有与源实例一致的 SID,并正确映射到已迁移的数据库用户。

DMS 为什么不迁移登录名

SQL Server 的安全对象分布在不同层级:

  • Login 位于实例级,通常存储在 master 数据库中,负责验证客户端能否连接 SQL Server。
  • User 位于具体业务数据库中,负责决定登录连接后能执行哪些操作。
  • SID 是连接 Login 和 User 的安全标识符。数据库用户会随数据库备份、还原或 DMS 复制一起迁移,但实例级 Login 不会自动出现在目标实例中。

DMS 不复制 master 数据库、服务器登录名和服务器级权限,是一种有意的安全边界,而不是单纯的功能缺口。

源环境和 Cloud SQL 运行在不同的管理模型下。一个源实例中拥有 sysadmin 权限的账号,不应该因为复制系统数据库,就自动获得托管云数据库中的同等权限。与此同时,自动复制密码哈希和服务器级安全凭据,也可能绕过企业对 PCI-DSS、SOC 2 等合规场景的审批和审计要求。

迁移过程还提供了清理旧凭据的机会。源实例中经常存在多年未使用的 SQL 登录名,直接全部复制到云端会扩大攻击面。对于适合企业身份体系的环境,可以进一步考虑使用 Customer-Managed Active Directory(CMAD)等集中式认证方案,逐步减少对传统 SQL 身份验证的依赖。

Login、User 与 SID 的关系

假设源实例中存在登录名 app_user,业务数据库中也存在同名用户:

客户端连接
    |
    v
Login: app_user  -- 实例级认证
    |
    | 通过 SID 映射
    v
User:  app_user  -- CustomerDB 中的数据库级授权

迁移 CustomerDB 后,数据库用户及其 SID 仍然存在。如果 Cloud SQL 的 master 数据库中没有对应的 app_user Login,应用无法完成实例级认证。如果目标端手工创建了同名 Login,但生成了不同的 SID,登录可以通过服务器认证,却无法自动对应到数据库用户,最终形成 orphaned user。

因此,迁移登录名时需要同时保留:

  • 原始登录名
  • 原始密码哈希
  • 原始 SID
  • 默认数据库等必要属性

用 sp_help_revlogin 生成登录迁移脚本

Microsoft 提供的 sp_help_revlogin 是迁移 SQL Server 登录名的经典工具。配合 sp_hexadecimal 使用时,它可以为源实例中的 SQL Server 身份验证登录名生成 CREATE LOGIN 语句,并包含原始的加密密码哈希和 SID。

1. 在源实例创建辅助存储过程

使用 SSMS 连接源 SQL Server,并在源实例的 master 数据库中执行 Microsoft 官方最新版脚本,创建以下两个过程:

  • master.dbo.sp_hexadecimal
  • master.dbo.sp_help_revlogin

这里不直接复制完整的 Microsoft 辅助脚本,因为脚本版本和 SQL Server 版本应以官方文档中的最新版为准。执行前确认脚本来自可信来源,并在变更流程中审查其内容。

2. 生成登录名脚本

在 SSMS 中将查询结果切换为 Results to Text,然后执行:

USE [master];
GO

EXEC master.dbo.sp_help_revlogin;
GO

过程会输出一组可以在目标实例执行的 T-SQL,形式类似于:

CREATE LOGIN [app_user]
WITH PASSWORD = 0x01004F3D... HASHED,
     SID = 0x8D2F...,
     DEFAULT_DATABASE = [CustomerDB];
GO

实际输出中的密码哈希和 SID 必须完整保留。不要手工截断、改写或将哈希转换成明文密码。建议把生成结果作为受控的迁移工件保存,并限制访问权限,因为它包含可用于恢复登录认证状态的敏感凭据材料。

3. 在 Cloud SQL 执行生成结果

确认目标 Cloud SQL for SQL Server 实例已经准备好,并且执行账号具备创建登录名所需的权限。将审核后的生成脚本连接到目标实例执行:

USE [master];
GO

CREATE LOGIN [app_user]
WITH PASSWORD = 0x01004F3D... HASHED,
     SID = 0x8D2F...,
     DEFAULT_DATABASE = [CustomerDB];
GO

上面的哈希和 SID 只是格式示例,不能直接用于生产环境。实际运行时必须使用 sp_help_revlogin 针对源实例生成的完整语句。

如果源数据库中的用户 SID 与目标 Login SID 一致,数据库用户就能自动映射到新实例中的登录名,应用通常无需修改密码即可完成切换。执行前后仍应根据 Cloud SQL 当前支持的权限模型和实例配置验证语句兼容性。

已经出现 orphaned user 怎么处理

如果团队在执行 sp_help_revlogin 之前,已经在目标端手工创建了同名登录名,目标 Login 很可能拥有不同的 SID。此时可以在对应的业务数据库中重新绑定用户和登录名:

USE [CustomerDB];
GO

ALTER USER [app_user] WITH LOGIN = [app_user];
GO

这条语句会更新数据库用户与服务器登录名之间的 SID 映射。执行后可以用以下查询确认用户和登录名的 SID 是否一致:

USE [CustomerDB];
GO

SELECT
    dp.name AS database_user,
    dp.sid AS database_user_sid,
    sp.name AS server_login,
    sp.sid AS server_login_sid
FROM sys.database_principals AS dp
LEFT JOIN master.sys.server_principals AS sp
    ON sp.sid = dp.sid
WHERE dp.name = N'app_user';
GO

如果查询不到对应的 server_login,说明目标实例仍缺少登录名,或者登录名的 SID 尚未正确修复。不要只根据用户名判断映射是否正确,SID 才是 SQL Server 识别两者关系的关键。

切换前的检查清单

可以把登录迁移放在正式 cutover 前的演练阶段完成,并逐项验证:

  • 源实例中只筛选仍在使用、且经过审批的 SQL 登录名。
  • sp_help_revlogin 使用 Microsoft 官方最新版脚本创建。
  • 生成的密码哈希和 SID 未被截断或格式化工具改写。
  • 目标 Cloud SQL 实例中的执行账号拥有所需权限。
  • 业务数据库用户与目标 Login 的 SID 已核对。
  • 应用连接池、默认数据库和最小权限配置已验证。
  • 迁移工件按密码凭据处理,限制存储、传输和日志暴露范围。
  • 对不再使用的旧登录名不进行迁移,或在迁移后立即禁用并清理。

迁移不是简单复制凭据

对于 lift-and-shift 项目,使用 sp_help_revlogin 保留密码哈希和 SID,是恢复应用连接、避免 orphaned user 的直接路径。但它也意味着源环境中的认证方式被延续到了云端。更长期的方案是结合 CMAD 等企业身份集成,集中管理账号生命周期、认证策略和审计记录。

因此,实际项目可以分两步推进:短期使用受控脚本完成无感切换,保证应用按时恢复;长期清理遗留 SQL 登录名,迁移到更适合云端治理的集中身份体系。DMS 负责数据库数据复制,登录名迁移则应作为独立、可审计的安全变更来设计。


相关推荐