SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019 安全机制 — 语法知识点及使用方法详解(18)

SQL Server 2019 安全机制 --- 语法知识点及使用方法详解


一、SQL Server 2019 安全性概述

1.1 SQL Server 2019 的安全机制简介

SQL Server 2019 采用层级化安全模型,从服务器实例 → 数据库 → 架构 → 对象,层层控制访问权限。核心安全组件包括:

  • 身份验证(Authentication):验证用户身份(登录名)。
  • 授权(Authorization):授予用户或角色对数据库对象的操作权限。
  • 角色(Roles):权限集合,便于批量授权。
  • 权限(Permissions):对特定对象(表、视图、存储过程等)的特定操作(SELECT, INSERT, EXECUTE等)。

📌 安全层级模型

  1. 服务器级别:登录名、服务器角色、端点、链接服务器等。
  2. 数据库级别:用户、数据库角色、应用程序角色、权限。
  3. 架构级别:对象(表、视图、过程等)归属的命名空间。
  4. 对象级别:具体对象的权限(列级权限也支持)。

1.2 基本安全术语

术语 说明
Principal(主体) 可请求 SQL Server 资源的实体,如登录名、用户、角色。
Securable(安全对象) 可被授予权限的资源,如表、视图、数据库、服务器等。
Permission(权限) 对安全对象执行操作的权利,如 SELECT、INSERT、ALTER 等。
Login(登录名) 服务器级别的主体,用于连接 SQL Server 实例。
User(用户) 数据库级别的主体,映射到登录名,用于访问特定数据库。
Role(角色) 权限容器,可包含用户或其他角色(数据库角色不支持嵌套)。

1.3 安全验证方式

SQL Server 支持两种身份验证模式:

1. Windows 身份验证模式
  • 用户通过 Windows 账户登录。
  • SQL Server 信任 Windows OS 的身份验证。
  • 更安全:密码策略、账户锁定、Kerberos 等由 Windows 管理。
  • 适用于企业内网环境。
2. 混合模式(SQL Server 和 Windows 身份验证模式)
  • 同时支持 Windows 账户和 SQL Server 登录账户(用户名/密码)。
  • 需要为 SQL 登录账户设置密码策略。
  • 适用于需要外部访问或遗留应用的场景。

⚠️ 注意 :混合模式下,sa 账户必须设置强密码!


1.4 设置验证模式

方法1:使用 SQL Server Management Studio (SSMS)

服务器属性 → 安全性 → 服务器身份验证 → 选择模式 → 重启服务。

方法2:使用 T-SQL 修改注册表(需重启)
sql 复制代码
-- 查看当前身份验证模式
SELECT SERVERPROPERTY('IsIntegratedSecurityOnly') AS AuthMode;
-- 1 = 仅 Windows 身份验证;0 = 混合模式

-- ⚠️ 修改身份验证模式(需 sysadmin 权限,修改后需重启 SQL Server 服务)
USE master;
GO
EXEC xp_instance_regwrite 
    N'HKEY_LOCAL_MACHINE', 
    N'Software\Microsoft\MSSQLServer\MSSQLServer',
    N'LoginMode', 
    REG_DWORD, 
    2; -- 1=仅Windows, 2=混合模式
GO
-- 重启 SQL Server 服务生效

📌 生产环境建议:优先使用 Windows 身份验证;如需混合模式,禁用或重命名 sa 账户。


二、SQL Server 2019 登录名

登录名是服务器级别的安全主体,用于建立与 SQL Server 实例的连接。


2.1 创建登录账户

语法:
sql 复制代码
-- 创建 SQL Server 登录账户
CREATE LOGIN login_name 
WITH PASSWORD = 'password' 
    [ , <option_list> ]

-- 创建 Windows 登录账户
CREATE LOGIN [Domain\UserName] FROM WINDOWS 
    [ WITH <windows_options> ]

<option_list> ::=
    MUST_CHANGE | CHECK_EXPIRATION = { ON | OFF } 
    | CHECK_POLICY = { ON | OFF } 
    | DEFAULT_DATABASE = database_name 
    | DEFAULT_LANGUAGE = language_name 
    | SID = sid

<windows_options> ::=
    DEFAULT_DATABASE = database_name 
    | DEFAULT_LANGUAGE = language_name
案例1:创建 SQL Server 登录账户
sql 复制代码
-- 创建 SQL 登录账户 'app_user',设置强密码和策略
CREATE LOGIN app_user 
WITH PASSWORD = 'P@ssw0rd!2025', 
    CHECK_POLICY = ON,      -- 强制Windows密码策略(复杂性、历史等)
    CHECK_EXPIRATION = ON,  -- 密码过期
    MUST_CHANGE = OFF,      -- 首次登录不要求改密码(生产环境建议ON)
    DEFAULT_DATABASE = [CompanyDB], -- 默认数据库
    DEFAULT_LANGUAGE = [us_english]; -- 默认语言

-- 验证创建
SELECT name, type_desc, create_date, is_disabled 
FROM sys.server_principals 
WHERE name = 'app_user';
GO

注释

  • CHECK_POLICY=ON:启用密码复杂性、长度、历史等策略(依赖Windows策略)。
  • CHECK_EXPIRATION=ON:密码会过期(需配合策略)。
  • DEFAULT_DATABASE:登录后自动切换的数据库。
案例2:创建 Windows 登录账户
sql 复制代码
-- 假设域为 MYDOMAIN,用户为 db_admin
CREATE LOGIN [MYDOMAIN\db_admin] FROM WINDOWS
WITH DEFAULT_DATABASE = [CompanyDB],
     DEFAULT_LANGUAGE = [us_english];

-- 验证
SELECT name, type_desc FROM sys.server_principals WHERE name LIKE '%db_admin%';
GO

注释

  • Windows 登录无需密码,由 AD 管理。
  • 更安全,推荐在域环境中使用。

2.2 修改登录账户

语法:
sql 复制代码
ALTER LOGIN login_name 
{
    <status_option> 
    | WITH <set_option> [ ,... ]
}

<status_option> ::=
    ENABLE | DISABLE

<set_option> ::=
    PASSWORD = 'password' | HASHED 'hashed_password'
    | NAME = new_login_name
    | CHECK_POLICY = { ON | OFF }
    | CHECK_EXPIRATION = { ON | OFF }
    | CREDENTIAL = credential_name
    | DEFAULT_DATABASE = database_name
    | DEFAULT_LANGUAGE = language_name
案例3:修改登录账户密码和状态
sql 复制代码
-- 修改 app_user 密码
ALTER LOGIN app_user 
WITH PASSWORD = 'NewP@ssw0rd!2025';

-- 禁用登录账户
ALTER LOGIN app_user DISABLE;

-- 启用登录账户
ALTER LOGIN app_user ENABLE;

-- 修改默认数据库
ALTER LOGIN app_user 
WITH DEFAULT_DATABASE = [master];

-- 验证状态
SELECT name, is_disabled, default_database_name 
FROM sys.server_principals 
WHERE name = 'app_user';
GO

注释

  • 修改密码后,旧密码立即失效。
  • DISABLE 可临时阻止用户登录,无需删除账户。

2.3 删除登录账户

语法:
sql 复制代码
DROP LOGIN login_name;
案例4:删除登录账户
sql 复制代码
-- 删除前检查是否映射到数据库用户(避免孤立用户)
USE CompanyDB;
GO
SELECT dp.name AS DBUser
FROM sys.database_principals dp
JOIN sys.server_principals sp ON dp.sid = sp.sid
WHERE sp.name = 'app_user';

-- 如果存在映射用户,先删除数据库用户(见后续章节)
-- USE CompanyDB;
-- DROP USER IF EXISTS app_user;

-- 删除登录账户
DROP LOGIN app_user;

-- 验证删除
SELECT name FROM sys.server_principals WHERE name = 'app_user';
GO

注释

  • 删除登录前,应先删除其在各数据库中映射的用户,避免产生孤立用户
  • 使用 DROP LOGIN IF EXISTS (SQL Server 2016+) 可避免错误。

三、SQL Server 2019 的角色与权限

角色是权限的集合,简化权限管理。


3.1 固定服务器角色

服务器角色在服务器级别分配权限,适用于整个实例。

角色名称 说明
sysadmin 系统管理员,拥有所有权限(慎用!)
serveradmin 配置服务器范围设置
setupadmin 管理链接服务器和启动过程
securityadmin 管理登录和权限
processadmin 管理进程(如KILL)
dbcreator 创建、修改、删除数据库
diskadmin 管理磁盘文件
bulkadmin 执行 BULK INSERT
案例5:将登录账户添加到固定服务器角色
sql 复制代码
-- 创建一个用于监控的登录账户
CREATE LOGIN monitor_user 
WITH PASSWORD = 'MonitorP@ss!2025', 
    CHECK_POLICY = ON;

-- 将其添加到 securityadmin 角色(可管理登录和权限)
ALTER SERVER ROLE securityadmin ADD MEMBER monitor_user;

-- 验证成员资格
SELECT 
    sp.name AS LoginName,
    sr.name AS ServerRole
FROM sys.server_role_members rm
JOIN sys.server_principals sp ON rm.member_principal_id = sp.principal_id
JOIN sys.server_principals sr ON rm.role_principal_id = sr.principal_id
WHERE sp.name = 'monitor_user';
GO

-- 移除角色成员(如需)
-- ALTER SERVER ROLE securityadmin DROP MEMBER monitor_user;

注释

  • sysadmin 角色拥有最高权限,应严格控制。
  • 使用 ALTER SERVER ROLE ... ADD MEMBER 添加成员(SQL Server 2012+)。
  • 旧语法 sp_addsrvrolemember 已弃用。

3.2 数据库角色

数据库角色在数据库级别分配权限。分为:

  • 固定数据库角色:预定义角色(如 db_owner, db_datareader)。
  • 用户自定义数据库角色:按需创建。
常用固定数据库角色:
角色名称 说明
db_owner 数据库所有者,拥有全部权限
db_securityadmin 管理数据库角色和权限
db_accessadmin 管理数据库访问(用户)
db_backupoperator 备份数据库
db_ddladmin 执行所有 DDL(CREATE/ALTER/DROP)
db_datareader 读取所有用户表数据
db_datawriter 修改所有用户表数据
db_denydatareader 拒绝读取数据
db_denydatawriter 拒绝修改数据
案例6:创建数据库用户并分配固定数据库角色
sql 复制代码
-- 假设已存在登录 'app_user'
USE CompanyDB;
GO

-- 创建数据库用户(映射到登录)
CREATE USER app_user FOR LOGIN app_user;

-- 将用户添加到 db_datareader 和 db_datawriter 角色
ALTER ROLE db_datareader ADD MEMBER app_user;
ALTER ROLE db_datawriter ADD MEMBER app_user;

-- 验证用户和角色成员
SELECT 
    dp.name AS UserName,
    dp.type_desc AS UserType,
    rp.name AS RoleName
FROM sys.database_role_members drm
JOIN sys.database_principals dp ON drm.member_principal_id = dp.principal_id
JOIN sys.database_principals rp ON drm.role_principal_id = rp.principal_id
WHERE dp.name = 'app_user';
GO

注释

  • 数据库用户必须映射到服务器登录名(Windows 或 SQL)。
  • ALTER ROLE ... ADD MEMBER 是推荐语法(SQL Server 2012+)。
  • sp_addrolemember 已弃用。

3.3 自定义数据库角色

当固定角色权限过大或不满足需求时,创建自定义角色。

语法:
sql 复制代码
-- 创建角色
CREATE ROLE role_name [ AUTHORIZATION owner_name ];

-- 删除角色
DROP ROLE role_name;

-- 添加成员
ALTER ROLE role_name ADD MEMBER user_name;

-- 移除成员
ALTER ROLE role_name DROP MEMBER user_name;
案例7:创建自定义角色并授予权限
sql 复制代码
USE CompanyDB;
GO

-- 创建自定义角色 'SalesManager'
CREATE ROLE SalesManager;

-- 授予对 Sales 相关表的权限
GRANT SELECT, INSERT, UPDATE ON dbo.Orders TO SalesManager;
GRANT SELECT, INSERT, UPDATE ON dbo.OrderDetails TO SalesManager;
GRANT EXECUTE ON dbo.usp_GetSalesReport TO SalesManager;

-- 创建用户并添加到自定义角色
CREATE USER sales_mgr_user FOR LOGIN sales_manager_login; -- 假设登录已存在
ALTER ROLE SalesManager ADD MEMBER sales_mgr_user;

-- 验证权限
EXECUTE AS USER = 'sales_mgr_user'; -- 切换执行上下文
SELECT * FROM dbo.Orders; -- 应成功
REVERT; -- 切换回原用户

-- 查看角色权限
SELECT 
    p.permission_name,
    p.state_desc,
    o.name AS ObjectName,
    r.name AS RoleName
FROM sys.database_permissions p
JOIN sys.database_principals r ON p.grantee_principal_id = r.principal_id
LEFT JOIN sys.objects o ON p.major_id = o.object_id
WHERE r.name = 'SalesManager';
GO

注释

  • 自定义角色允许精确控制权限。
  • 使用 GRANT 语句向角色授予权限,成员自动继承。
  • EXECUTE ASREVERT 用于测试权限。

3.4 应用程序角色

应用程序角色是一种特殊数据库角色,没有成员,通过密码激活。激活后,连接的安全上下文切换为该角色,原有用户权限被忽略。

适用于应用程序连接池,确保统一权限,避免用户权限泄露。

语法:
sql 复制代码
-- 创建应用程序角色
CREATE APPLICATION ROLE app_role_name 
WITH PASSWORD = 'password' [, DEFAULT_SCHEMA = schema_name];

-- 激活应用程序角色(在连接中执行)
EXEC sp_setapprole @rolename = 'app_role_name', @password = 'password';

-- 修改应用程序角色
ALTER APPLICATION ROLE app_role_name 
WITH NAME = new_name, 
     PASSWORD = 'new_password' 
     [, DEFAULT_SCHEMA = new_schema];

-- 删除应用程序角色
DROP APPLICATION ROLE app_role_name;
案例8:创建和使用应用程序角色
sql 复制代码
USE CompanyDB;
GO

-- 创建应用程序角色 'AppSalesRole'
CREATE APPLICATION ROLE AppSalesRole 
WITH PASSWORD = 'AppSalesP@ss!2025', 
     DEFAULT_SCHEMA = dbo;

-- 向应用程序角色授予权限
GRANT SELECT ON dbo.Products TO AppSalesRole;
GRANT SELECT, INSERT, UPDATE ON dbo.Orders TO AppSalesRole;
GRANT EXECUTE ON dbo.usp_CreateOrder TO AppSalesRole;

-- 模拟应用程序激活角色
-- 假设当前连接是 'app_user'(拥有db_datareader权限)
PRINT '激活前用户: ' + USER_NAME();

-- 激活应用程序角色(密码正确)
EXEC sp_setapprole @rolename = 'AppSalesRole', @password = 'AppSalesP@ss!2025';

PRINT '激活后用户: ' + USER_NAME(); -- 显示角色名,不再是原用户

-- 测试权限:应能访问Orders,但不能访问未授权的表(如Employees)
SELECT TOP 1 * FROM dbo.Orders; -- 成功
-- SELECT * FROM dbo.Employees; -- 失败!权限不足

-- ⚠️ 注意:激活后无法撤销,连接关闭前一直有效
-- 无直接撤销命令,需断开连接

-- 查看应用程序角色
SELECT name, default_schema_name 
FROM sys.database_principals 
WHERE type = 'A'; -- A = 应用程序角色
GO

注释

  • 激活后,USER_NAME() 返回角色名。
  • 原用户权限完全被角色权限替代。
  • 适用于中间层应用程序,确保最小权限原则。

3.5 将登录指派到角色

登录名通过映射为数据库用户,再加入数据库角色。

案例9:完整流程 --- 登录 → 用户 → 角色
sql 复制代码
-- 1. 创建登录
CREATE LOGIN report_user 
WITH PASSWORD = 'ReportP@ss!2025', 
    CHECK_POLICY = ON;

-- 2. 在目标数据库创建用户
USE CompanyDB;
GO
CREATE USER report_user FOR LOGIN report_user;

-- 3. 创建自定义角色(如需要)
CREATE ROLE ReportViewer;
GRANT SELECT ON SCHEMA::dbo TO ReportViewer; -- 授予整个架构的SELECT权限

-- 4. 将用户添加到角色
ALTER ROLE ReportViewer ADD MEMBER report_user;
-- 或添加到固定角色
-- ALTER ROLE db_datareader ADD MEMBER report_user;

-- 5. 验证
EXECUTE AS USER = 'report_user';
SELECT USER_NAME(); -- 应为 'report_user'
SELECT * FROM dbo.Products; -- 应成功(如果在ReportViewer或db_datareader中)
REVERT;
GO

注释

  • 登录名是服务器级,用户是数据库级。
  • 一个登录名可在多个数据库中创建用户。
  • 角色是权限的便捷管理方式。

3.6 将角色指派到多个登录账户

一个角色可包含多个用户,实现批量授权。

案例10:将自定义角色分配给多个用户
sql 复制代码
USE CompanyDB;
GO

-- 假设已创建角色 'DataEntryClerk'
CREATE ROLE DataEntryClerk;
GRANT INSERT, UPDATE ON dbo.Customers TO DataEntryClerk;
GRANT INSERT, UPDATE ON dbo.Orders TO DataEntryClerk;

-- 创建多个登录和用户
CREATE LOGIN clerk1 WITH PASSWORD = 'ClerkP@ss1';
CREATE LOGIN clerk2 WITH PASSWORD = 'ClerkP@ss2';
CREATE USER clerk1 FOR LOGIN clerk1;
CREATE USER clerk2 FOR LOGIN clerk2;

-- 将多个用户添加到同一角色
ALTER ROLE DataEntryClerk ADD MEMBER clerk1;
ALTER ROLE DataEntryClerk ADD MEMBER clerk2;

-- 验证
SELECT 
    dp.name AS UserName,
    rp.name AS RoleName
FROM sys.database_role_members drm
JOIN sys.database_principals dp ON drm.member_principal_id = dp.principal_id
JOIN sys.database_principals rp ON drm.role_principal_id = rp.principal_id
WHERE rp.name = 'DataEntryClerk';
GO

注释

  • 修改角色权限(如 GRANT/REVOKE)会自动影响所有成员。
  • 管理高效:添加/移除用户即可控制权限。

四、权限管理

权限是安全机制的核心,控制主体对安全对象的操作。


4.1 权限类型

  • 语句权限:如 CREATE TABLE, CREATE VIEW(数据库级)。
  • 对象权限:如 SELECT, INSERT, UPDATE, DELETE, EXECUTE(对象级)。
  • 隐式权限:如 db_owner 角色拥有所有权限。

4.2 GRANT、REVOKE、DENY 语法

sql 复制代码
-- 授予权限
GRANT { ALL [ PRIVILEGES ] } 
    | permission [ ( column [ ,...n ] ) ] [ ,...n ]
    ON [ class:: ] securable 
    TO principal [ ,...n ] 
    [ WITH GRANT OPTION ] -- 允许被授权者再授权
    [ AS principal ] -- 指定授权者

-- 撤销权限
REVOKE [ GRANT OPTION FOR ] 
    { [ ALL [ PRIVILEGES ] ] | permission [ ( column [ ,...n ] ) ] [ ,...n ] }
    ON [ class:: ] securable 
    FROM principal [ ,...n ] 
    [ CASCADE ] -- 级联撤销(撤销被授权者再授予的权限)
    [ AS principal ]

-- 拒绝权限(显式拒绝,优先级高于GRANT)
DENY { ALL [ PRIVILEGES ] } 
    | permission [ ( column [ ,...n ] ) ] [ ,...n ]
    ON [ class:: ] securable 
    TO principal [ ,...n ] 
    [ CASCADE ]
    [ AS principal ]

📌 权限优先级DENY > GRANT


案例11:精细化权限控制 --- 列级权限
sql 复制代码
USE CompanyDB;
GO

-- 创建测试用户
CREATE LOGIN sensitive_user WITH PASSWORD = 'SensitiveP@ss!2025';
CREATE USER sensitive_user FOR LOGIN sensitive_user;

-- 假设 Employees 表有敏感列 Salary 和非敏感列 FirstName, LastName
-- 授予查看姓名权限,拒绝查看薪资权限
GRANT SELECT (EmployeeID, FirstName, LastName) ON dbo.Employees TO sensitive_user;
DENY SELECT (Salary) ON dbo.Employees TO sensitive_user;

-- 测试权限
EXECUTE AS USER = 'sensitive_user';
SELECT EmployeeID, FirstName, LastName FROM dbo.Employees; -- 成功
-- SELECT Salary FROM dbo.Employees; -- 失败!被拒绝
-- SELECT * FROM dbo.Employees; -- 失败!包含Salary列
REVERT;

-- 查看列级权限
SELECT 
    p.permission_name,
    p.state_desc,
    c.name AS ColumnName,
    o.name AS ObjectName
FROM sys.database_permissions p
JOIN sys.objects o ON p.major_id = o.object_id
JOIN sys.columns c ON p.minor_id = c.column_id AND c.object_id = o.object_id
WHERE p.grantee_principal_id = USER_ID('sensitive_user')
    AND o.name = 'Employees';
GO

注释

  • DENY 显式拒绝权限,即使通过角色获得 GRANT 也会被覆盖。
  • 列级权限提供更细粒度的控制。
  • SELECT * 如果包含被拒绝的列,整个语句失败。

案例12:使用 WITH GRANT OPTION 和 CASCADE
sql 复制代码
USE CompanyDB;
GO

-- 创建用户A、B、C
CREATE LOGIN userA WITH PASSWORD = 'UserAP@ss';
CREATE LOGIN userB WITH PASSWORD = 'UserBP@ss';
CREATE LOGIN userC WITH PASSWORD = 'UserCP@ss';
CREATE USER userA FOR LOGIN userA;
CREATE USER userB FOR LOGIN userB;
CREATE USER userC FOR LOGIN userC;

-- 授予 userA SELECT 权限,并允许其再授权
GRANT SELECT ON dbo.Products TO userA WITH GRANT OPTION;

-- userA 授权给 userB
EXECUTE AS USER = 'userA';
GRANT SELECT ON dbo.Products TO userB;
REVERT;

-- userB 授权给 userC
EXECUTE AS USER = 'userB';
GRANT SELECT ON dbo.Products TO userC;
REVERT;

-- 查看授权链
SELECT 
    grantor.name AS Grantor,
    grantee.name AS Grantee,
    p.permission_name,
    p.state_desc
FROM sys.database_permissions p
JOIN sys.database_principals grantor ON p.grantor_principal_id = grantor.principal_id
JOIN sys.database_principals grantee ON p.grantee_principal_id = grantee.principal_id
WHERE p.major_id = OBJECT_ID('dbo.Products')
    AND p.permission_name = 'SELECT';

-- 撤销 userA 的权限,并级联撤销(userB和userC的权限也会被撤销)
REVOKE GRANT OPTION FOR SELECT ON dbo.Products FROM userA CASCADE;

-- 再次查看,userB和userC的权限应已消失
SELECT 
    grantor.name AS Grantor,
    grantee.name AS Grantee,
    p.permission_name,
    p.state_desc
FROM sys.database_permissions p
JOIN sys.database_principals grantor ON p.grantor_principal_id = grantor.principal_id
JOIN sys.database_principals grantee ON p.grantee_principal_id = grantee.principal_id
WHERE p.major_id = OBJECT_ID('dbo.Products')
    AND p.permission_name = 'SELECT';
GO

注释

  • WITH GRANT OPTION 允许被授权者再授权。
  • CASCADEREVOKE 时撤销所有下级授权。
  • 适用于需要委派权限管理的场景。

五、综合性案例

综合案例1:构建安全的多角色销售数据库

sql 复制代码
-- 场景:为销售部门设计安全架构,包含经理、普通员工、报表用户

USE master;
GO

-- 步骤1:创建登录账户
CREATE LOGIN sales_manager_login WITH PASSWORD = 'MgrP@ss!2025', CHECK_POLICY = ON;
CREATE LOGIN sales_clerk_login WITH PASSWORD = 'ClerkP@ss!2025', CHECK_POLICY = ON;
CREATE LOGIN sales_report_login WITH PASSWORD = 'ReportP@ss!2025', CHECK_POLICY = ON;
CREATE LOGIN sales_app_login WITH PASSWORD = 'AppP@ss!2025', CHECK_POLICY = ON;
GO

USE CompanyDB;
GO

-- 步骤2:创建数据库用户
CREATE USER sales_manager FOR LOGIN sales_manager_login;
CREATE USER sales_clerk FOR LOGIN sales_clerk_login;
CREATE USER sales_report FOR LOGIN sales_report_login;
CREATE USER sales_app FOR LOGIN sales_app_login;
GO

-- 步骤3:创建自定义角色
-- 销售经理角色:可管理销售数据和查看报表
CREATE ROLE SalesManager;
GRANT SELECT, INSERT, UPDATE, DELETE ON dbo.Orders TO SalesManager;
GRANT SELECT, INSERT, UPDATE, DELETE ON dbo.OrderDetails TO SalesManager;
GRANT EXECUTE ON dbo.usp_GetSalesReport TO SalesManager;
GRANT EXECUTE ON dbo.usp_UpdateOrderStatus TO SalesManager;

-- 销售员工角色:只能创建和修改自己的订单
CREATE ROLE SalesClerk;
GRANT SELECT, INSERT, UPDATE ON dbo.Orders TO SalesClerk; -- 假设有 OwnerID 列
GRANT SELECT, INSERT, UPDATE ON dbo.OrderDetails TO SalesClerk;
DENY DELETE ON dbo.Orders TO SalesClerk; -- 禁止删除订单
DENY DELETE ON dbo.OrderDetails TO SalesClerk;

-- 报表角色:只读访问
CREATE ROLE SalesReporter;
GRANT SELECT ON dbo.Orders TO SalesReporter;
GRANT SELECT ON dbo.OrderDetails TO SalesReporter;
GRANT SELECT ON dbo.Products TO SalesReporter;
GRANT EXECUTE ON dbo.usp_GetSalesReport TO SalesReporter;

-- 步骤4:分配用户到角色
ALTER ROLE SalesManager ADD MEMBER sales_manager;
ALTER ROLE SalesClerk ADD MEMBER sales_clerk;
ALTER ROLE SalesReporter ADD MEMBER sales_report;

-- 步骤5:创建应用程序角色(用于Web应用)
CREATE APPLICATION ROLE SalesAppRole 
WITH PASSWORD = 'WebAppSecret!2025';
GRANT SELECT, INSERT, UPDATE ON dbo.Orders TO SalesAppRole;
GRANT SELECT, INSERT, UPDATE ON dbo.OrderDetails TO SalesAppRole;
GRANT EXECUTE ON dbo.usp_CreateOrder TO SalesAppRole;
GRANT EXECUTE ON dbo.usp_UpdateOrder TO SalesAppRole;

-- 步骤6:验证权限
PRINT '=== 验证 SalesManager ===';
EXECUTE AS USER = 'sales_manager';
SELECT USER_NAME();
EXEC dbo.usp_GetSalesReport; -- 应成功
DELETE FROM dbo.Orders WHERE OrderID = 1; -- 应成功(如果存在)
REVERT;

PRINT '=== 验证 SalesClerk ===';
EXECUTE AS USER = 'sales_clerk';
SELECT USER_NAME();
INSERT INTO dbo.Orders (CustomerID, OrderDate) VALUES (1, GETDATE()); -- 应成功
-- DELETE FROM dbo.Orders WHERE OrderID = 1; -- 应失败!被拒绝
REVERT;

PRINT '=== 验证 SalesReporter ===';
EXECUTE AS USER = 'sales_report';
SELECT USER_NAME();
SELECT TOP 1 * FROM dbo.Products; -- 应成功
-- UPDATE dbo.Products SET Price = 100 WHERE ProductID = 1; -- 应失败!无权限
REVERT;

PRINT '=== 验证 SalesAppRole ===';
EXECUTE AS USER = 'sales_app';
EXEC sp_setapprole @rolename = 'SalesAppRole', @password = 'WebAppSecret!2025';
SELECT USER_NAME(); -- 应显示 'SalesAppRole'
EXEC dbo.usp_CreateOrder @CustomerID = 1, @ProductID = 1, @Quantity = 2; -- 假设存储过程存在
REVERT;
GO

-- 步骤7:查看安全配置摘要
SELECT 
    'User' AS Type, 
    dp.name AS Name, 
    dp.type_desc,
    STRING_AGG(rp.name, ', ') AS Roles
FROM sys.database_principals dp
LEFT JOIN sys.database_role_members drm ON dp.principal_id = drm.member_principal_id
LEFT JOIN sys.database_principals rp ON drm.role_principal_id = rp.principal_id
WHERE dp.type IN ('S', 'U') -- SQL user, Windows user
    AND dp.name NOT IN ('dbo', 'guest', 'INFORMATION_SCHEMA', 'sys')
GROUP BY dp.name, dp.type_desc

UNION ALL

SELECT 
    'App Role' AS Type, 
    name AS Name, 
    type_desc, 
    NULL AS Roles
FROM sys.database_principals 
WHERE type = 'A' -- Application role
ORDER BY Type, Name;
GO

注释

  • 设计了三种用户角色和一个应用程序角色。
  • 使用 DENY 明确禁止销售员工删除订单。
  • 应用程序角色用于Web应用,确保统一权限。
  • 通过 EXECUTE ASREVERT 测试权限。
  • 最后查询汇总了数据库安全配置。

综合案例2:数据库安全审计与合规加固

sql 复制代码
-- 场景:实施安全审计策略,加固数据库安全

USE master;
GO

-- 1. 创建审计登录和用户
CREATE LOGIN auditor_login WITH PASSWORD = 'AuditP@ss!2025', CHECK_POLICY = ON;
GO

USE CompanyDB;
GO
CREATE USER auditor FOR LOGIN auditor_login;
GO

-- 2. 创建审计角色并授予权限(只读 + 查看定义)
CREATE ROLE Auditor;
GRANT SELECT ON SCHEMA::dbo TO Auditor; -- 读取数据
GRANT VIEW DEFINITION TO Auditor; -- 查看对象定义(存储过程、函数等)
GRANT EXECUTE ON dbo.usp_GetAuditLog TO Auditor; -- 假设有审计存储过程
ALTER ROLE Auditor ADD MEMBER auditor;
GO

-- 3. 禁用或重命名 sa 账户(混合模式下)
-- 查找 sa 登录名(可能被重命名)
DECLARE @sa_name NVARCHAR(128);
SELECT @sa_name = name FROM sys.server_principals WHERE sid = 0x01;

-- 禁用 sa
IF @sa_name IS NOT NULL
BEGIN
    PRINT '禁用 sa 账户: ' + @sa_name;
    ALTER LOGIN [@sa_name] DISABLE; -- 使用方括号处理特殊字符
END
GO

-- 4. 强制密码策略(检查所有SQL登录)
-- 生成修改脚本
SELECT 
    'ALTER LOGIN ' + QUOTENAME(name) + 
    ' WITH CHECK_POLICY = ON, CHECK_EXPIRATION = ON;' AS FixScript
FROM sys.sql_logins
WHERE is_policy_checked = 0 OR is_expiration_checked = 0
    AND name NOT LIKE '##%'; -- 排除证书登录

-- 5. 创建登录失败审计(服务器级别触发器)
-- 创建审计表
USE master;
GO
CREATE TABLE dbo.LoginAudit (
    AuditID INT IDENTITY(1,1) PRIMARY KEY,
    EventTime DATETIME2(3) DEFAULT SYSDATETIME(),
    EventType NVARCHAR(100),
    LoginName NVARCHAR(128),
    ClientHost NVARCHAR(128),
    AppName NVARCHAR(128),
    Success BIT -- 1=成功, 0=失败
);
GO

-- 创建登录审计触发器(SQL Server 2005+)
CREATE TRIGGER trg_ServerAudit_Logon
ON ALL SERVER
FOR LOGON
AS
BEGIN
    DECLARE @EventData XML = EVENTDATA();
    DECLARE @LoginName NVARCHAR(128) = @EventData.value('(/EVENT_INSTANCE/LoginName)[1]', 'NVARCHAR(128)');
    DECLARE @ClientHost NVARCHAR(128) = @EventData.value('(/EVENT_INSTANCE/ClientHost)[1]', 'NVARCHAR(128)');
    DECLARE @AppName NVARCHAR(128) = @EventData.value('(/EVENT_INSTANCE/ApplicationName)[1]', 'NVARCHAR(128)');
    DECLARE @IsSuccess BIT = 1; -- 假设成功

    -- 检查是否登录失败(触发器在登录后执行,无法直接捕获失败)
    -- 实际中,登录失败审计需使用 SQL Server Audit 或 Extended Events
    -- 此处仅记录成功登录(演示目的)

    INSERT INTO master.dbo.LoginAudit (EventType, LoginName, ClientHost, AppName, Success)
    VALUES ('LOGON', @LoginName, @ClientHost, @AppName, @IsSuccess);
END
GO

-- 6. 启用服务器触发器
ENABLE TRIGGER trg_ServerAudit_Logon ON ALL SERVER;
GO

-- 7. 测试审计
-- 连接数据库(会触发审计)
SELECT * FROM master.dbo.LoginAudit ORDER BY EventTime DESC;
GO

-- 8. 清理(可选)
-- DISABLE TRIGGER trg_ServerAudit_Logon ON ALL SERVER;
-- DROP TRIGGER trg_ServerAudit_Logon ON ALL SERVER;
-- DROP TABLE master.dbo.LoginAudit;
GO

注释

  • 创建专用审计账户,限制权限(最小权限原则)。
  • 禁用 sa 账户是重要安全加固措施。
  • 强制所有 SQL 登录使用密码策略。
  • 使用服务器触发器审计登录事件(实际生产建议用 SQL Server Audit)。
  • 此案例展示了安全加固的常见步骤。

六、最佳实践与注意事项

✅ 最佳实践:

  1. 最小权限原则:只授予完成工作所需的最小权限。
  2. 使用角色:通过角色管理权限,而非直接授权给用户。
  3. 优先 Windows 身份验证:更安全,便于集中管理。
  4. 禁用 sa 账户:混合模式下,禁用或重命名 sa 并设置强密码。
  5. 强制密码策略 :对 SQL 登录启用 CHECK_POLICYCHECK_EXPIRATION
  6. 定期审计:审查登录账户、用户、权限分配。
  7. 使用应用程序角色:中间层应用使用应用角色,隔离用户权限。
  8. 避免 sysadmin 滥用:严格控制 sysadmin 角色成员。

⚠️ 注意事项:

  1. 权限继承:用户权限 = 用户直接权限 + 所有角色权限 - DENY 权限。
  2. DENY 优先级:DENY 会覆盖 GRANT,即使来自不同角色。
  3. 孤立用户 :删除登录前,先删除其数据库用户,或使用 ALTER USER ... WITH LOGIN 修复。
  4. 触发器审计局限:LOGON 触发器无法捕获失败登录,应使用 SQL Server Audit。
  5. 性能影响:过多的权限检查和触发器可能影响性能。
  6. 备份安全:备份文件也需保护,防止数据泄露。
  7. 加密:考虑使用 TDE(透明数据加密)或列级加密保护敏感数据。

✅ 本章全面覆盖 SQL Server 2019 安全机制的核心内容,从身份验证、登录名、角色到精细化权限管理,并提供实战案例。合理配置安全策略是保护数据资产的关键!

相关推荐
Francek Chen1 小时前
【大数据存储与管理】分布式数据库HBase:03 HBase数据模型
大数据·数据库·hadoop·分布式·hdfs·hbase
做cv的小昊3 小时前
大语言模型系统:【CMU 11-868】课程学习笔记02——GPU编程基础1(GPU Programming Basics 1)
人工智能·笔记·学习·语言模型·llm·transformer·agent
小吴编程之路8 小时前
MySQL 索引核心特性深度解析:从底层原理到实操应用
数据库·mysql
炽烈小老头8 小时前
【每天学习一点算法 2026/03/08】相交链表
学习·算法·链表
~莫子8 小时前
MySQL集群技术
数据库·mysql
凤山老林8 小时前
SpringBoot 使用 H2 文本数据库构建轻量级应用
java·数据库·spring boot·后端
就不掉头发9 小时前
Linux与数据库进阶
数据库
与衫9 小时前
Gudu SQL Omni 技术深度解析
数据库·sql
咖啡の猫9 小时前
Redis桌面客户端
数据库·redis·缓存
oradh9 小时前
Oracle 11g数据库软件和数据库静默安装
数据库·oracle