SQL Server 2019 安全机制 --- 语法知识点及使用方法详解
一、SQL Server 2019 安全性概述
1.1 SQL Server 2019 的安全机制简介
SQL Server 2019 采用层级化安全模型,从服务器实例 → 数据库 → 架构 → 对象,层层控制访问权限。核心安全组件包括:
- 身份验证(Authentication):验证用户身份(登录名)。
- 授权(Authorization):授予用户或角色对数据库对象的操作权限。
- 角色(Roles):权限集合,便于批量授权。
- 权限(Permissions):对特定对象(表、视图、存储过程等)的特定操作(SELECT, INSERT, EXECUTE等)。
📌 安全层级模型:
- 服务器级别:登录名、服务器角色、端点、链接服务器等。
- 数据库级别:用户、数据库角色、应用程序角色、权限。
- 架构级别:对象(表、视图、过程等)归属的命名空间。
- 对象级别:具体对象的权限(列级权限也支持)。
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 AS和REVERT用于测试权限。
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允许被授权者再授权。CASCADE在REVOKE时撤销所有下级授权。- 适用于需要委派权限管理的场景。
五、综合性案例
综合案例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 AS和REVERT测试权限。- 最后查询汇总了数据库安全配置。
综合案例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)。
- 此案例展示了安全加固的常见步骤。
六、最佳实践与注意事项
✅ 最佳实践:
- 最小权限原则:只授予完成工作所需的最小权限。
- 使用角色:通过角色管理权限,而非直接授权给用户。
- 优先 Windows 身份验证:更安全,便于集中管理。
- 禁用 sa 账户:混合模式下,禁用或重命名 sa 并设置强密码。
- 强制密码策略 :对 SQL 登录启用
CHECK_POLICY和CHECK_EXPIRATION。 - 定期审计:审查登录账户、用户、权限分配。
- 使用应用程序角色:中间层应用使用应用角色,隔离用户权限。
- 避免 sysadmin 滥用:严格控制 sysadmin 角色成员。
⚠️ 注意事项:
- 权限继承:用户权限 = 用户直接权限 + 所有角色权限 - DENY 权限。
- DENY 优先级:DENY 会覆盖 GRANT,即使来自不同角色。
- 孤立用户 :删除登录前,先删除其数据库用户,或使用
ALTER USER ... WITH LOGIN修复。 - 触发器审计局限:LOGON 触发器无法捕获失败登录,应使用 SQL Server Audit。
- 性能影响:过多的权限检查和触发器可能影响性能。
- 备份安全:备份文件也需保护,防止数据泄露。
- 加密:考虑使用 TDE(透明数据加密)或列级加密保护敏感数据。
✅ 本章全面覆盖 SQL Server 2019 安全机制的核心内容,从身份验证、登录名、角色到精细化权限管理,并提供实战案例。合理配置安全策略是保护数据资产的关键!