Nat*_*han 5 sql-server permissions sql-server-2008-r2 service-broker signature
我有一个TheNotificationProcedure
对 msdb.dbo.sp_send_dbmail 进行跨数据库调用的过程。
它从服务代理队列中(间接地)调用:
CREATE QUEUE [Blah].[TheQueue]
WITH ACTIVATION (STATUS = ON, PROCEDURE_NAME = [Blah].[TheQueueProcedure],
MAX_QUEUE_READERS = 1, EXECUTE AS N'TheUser');
Run Code Online (Sandbox Code Playgroud)
TheQueueProcedure
最终打电话 TheNotificationProcedure
如果我在 SSMS 中连接TheUser
并执行TheNotificationProcedure
,则一切正常,电子邮件会发送出去。
但是,当TheNotificationProcedure
由于消息到达队列而被调用时,由于无法访问 msdb 过程而失败。
我已经尝试了所有我能想到的方法,包括在 msdb 中创建我自己的程序来包装 sp_send_dbmail 并签署我的 dbmail 包装器和TheNotificationProcedure
相同的证书,并确保 msdb 中的证书用户是“DatabaseMailUserRole”的成员。
最后,在做了许多更详细的跟踪之后,我最终注意到了以下几点:
也就是说,即使服务经纪人下执行登录的TheUser
,由于某种原因,它是数据库下执行用户的guest
,我怀疑至少部分地解释了我的权限问题。
该登录 TheUser
也映射到一个用户在MSDB叫TheUser
-它肯定是不映射到来宾。
那么为什么它在通过服务代理时在 msdb 中作为来宾执行呢?
我需要避免将数据库标记为Trustworthy
. 我希望通过签署程序(例如http://www.sommarskog.se/grantperm.html)我可以获得跨数据库传输的权限 - 是否execute as
否定了通常通过证书用户关联的任何权限?
这是一个脚本,可以在通过服务代理时在没有任何模块签名(提供相同的“访客”跟踪)的情况下复制上述权限问题:
设置:
--REPLACE EMAIL, and db_mail profile
--@profile_name = 'Test db mail profile',
--@recipients = 'test@test.test',
use master;
GO
IF EXISTS(select * FROM sys.databases where name='http://dba.stackexchange.com/questions/166033')
BEGIN
ALTER DATABASE [http://dba.stackexchange.com/questions/166033] SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE [http://dba.stackexchange.com/questions/166033] SET ONLINE WITH ROLLBACK IMMEDIATE;
DROP DATABASE [http://dba.stackexchange.com/questions/166033];
END
CREATE DATABASE [http://dba.stackexchange.com/questions/166033];
GO
IF EXISTS(select * FROM sys.server_principals WHERE name = 'TheUser' AND type_desc='SQL_LOGIN') DROP LOGIN TheUser;
CREATE LOGIN [TheUser] WITH PASSWORD=N'jL839lIFKttcm3cNuk1WUazfk5lS76RKMscZ01UdFkI='
, DEFAULT_DATABASE=[http://dba.stackexchange.com/questions/166033]
, DEFAULT_LANGUAGE=[us_english], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF;
use [msdb];
GO
IF (NOT EXISTS(select * FROM sys.database_principals WHERE name = 'TheUser')) CREATE USER [TheUser] FOR LOGIN [TheUser] WITH DEFAULT_SCHEMA=[dbo];
exec sp_addrolemember 'DatabaseMailUserRole', 'TheUser';
GO
use [http://dba.stackexchange.com/questions/166033];
GO
CREATE USER [TheUser] FOR LOGIN [TheUser] WITH DEFAULT_SCHEMA=[dbo]
GO
CREATE SCHEMA [Blah] AUTHORIZATION dbo;
GO
CREATE QUEUE [Blah].[SourceQueue];
GO
CREATE SERVICE [//FromService]
AUTHORIZATION [dbo]
ON QUEUE [Blah].[SourceQueue];
GO
CREATE MESSAGE TYPE [//TestMessage]
AUTHORIZATION [dbo]
VALIDATION = NONE;
GO
CREATE CONTRACT [//ServiceContract]
AUTHORIZATION [dbo]
([//TestMessage] SENT BY INITIATOR);
GO
CREATE PROCEDURE [Blah].[SendMessage]
AS
DECLARE @message varchar(50),
@conversationHandle UNIQUEIDENTIFIER
SET @message = 'Test Message Content';
-- Begin the dialog.
BEGIN DIALOG CONVERSATION @conversationHandle
FROM SERVICE [//FromService]
TO SERVICE '//ToService'
ON CONTRACT [//ServiceContract]
WITH ENCRYPTION = OFF;
-- Send the message on the dialog.
SEND ON CONVERSATION @conversationHandle
MESSAGE TYPE [//TestMessage]
(@message) ;
END CONVERSATION @conversationHandle ;
GO
CREATE PROCEDURE [dbo].[TheNotificationProcedure]
AS
PRINT 'DEBUG - Entering [dbo].[TheNotificationProcedure]'
-- Send notification
PRINT 'DEBUG - [dbo].[TheNotificationProcedure] - PRIOR TO msdb.dbo.sp_send_dbmail'
declare @log nvarchar(max) = '';
select @log = @log + 'name: ' + name + ' ' + 'type: ' + type + ' usage: ' + usage + ' || ' FROM sys.login_token
print @log
declare @mailitem_id int;
--exec [msdb].[dbo].[WRAP__sp_send_dbmail]
exec [msdb].[dbo].[sp_send_dbmail]
@profile_name = 'Test db mail profile',
@recipients = 'test@test.test', --@Recipient,
@subject = 'Testing sp_send_dbmail', --@NotificationSubject,
@body = 'Testing sp_sdend_dbmail from service broker', --@NotificationBody,
@exclude_query_output = 1,
@mailitem_id = @mailitem_id OUTPUT
PRINT 'DEBUG - [dbo].[TheNotificationProcedure] - AFTER msdb.dbo.sp_send_dbmail'
GO
CREATE PROCEDURE [Blah].[TestMessageHandler]
AS
--has other logic that eventully calls notification
EXECUTE [dbo].[TheNotificationProcedure]
GO
CREATE PROCEDURE [Blah].[TheQueueProcedure]
AS
--Service Broker variables
DECLARE @conversation_handle UNIQUEIDENTIFIER,
@conversation_group_id UNIQUEIDENTIFIER,
@message_body varchar(255),
@message_type_name NVARCHAR(256),
@dialog UNIQUEIDENTIFIER,
@RowsReceived int
PRINT 'Start'
WHILE (1 = 1)
BEGIN
-- Get next conversation group.
WAITFOR(
GET CONVERSATION GROUP @conversation_group_id FROM [Blah].[TheQueue]),
TIMEOUT 500 ;
-- If there are no more conversation groups, roll back the
-- transaction and break out of the outermost WHILE loop.
IF @conversation_group_id IS NULL
BEGIN
BREAK ;
END ;
WHILE (1 = 1)
BEGIN
BEGIN TRANSACTION
PRINT 'Get Message'
; RECEIVE TOP (1)
@dialog = conversation_handle,
@message_type_name=message_type_name,
@message_body=message_body
FROM [Blah].[TheQueue]
WHERE conversation_group_id = @conversation_group_id ;
SET @RowsReceived = @@ROWCOUNT
PRINT 'Queue Read: ' + ISNULL(@message_body, '<NULL>')
PRINT '@RowsReceived: ' + CAST(@RowsReceived as varchar(200))
IF (@RowsReceived = 0)
BEGIN
BREAK ;
END ;
PRINT 'Deal with Message'
IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
BEGIN
PRINT 'End Dialog received for dialog # ' + cast(@dialog as nvarchar(40)) ;
END CONVERSATION @dialog ;
END ;
IF (@message_type_name = '//TestMessage')
BEGIN
print 'Have //TestMessage: ' + @message_body
exec [Blah].[TestMessageHandler];
END
COMMIT TRANSACTION;
END
END
RETURN
GO
CREATE QUEUE [Blah].[TheQueue]
WITH ACTIVATION (STATUS = ON, PROCEDURE_NAME = [Blah].[TheQueueProcedure], MAX_QUEUE_READERS = 1, EXECUTE AS N'TheUser');
GO
CREATE SERVICE [//ToService]
AUTHORIZATION [dbo]
ON QUEUE [Blah].[TheQueue]
([//ServiceContract]);
GO
GRANT EXECUTE ON [Blah].[TheQueueProcedure] TO [TheUser];
GO
Run Code Online (Sandbox Code Playgroud)
然后开始一切:
--kick everything off
EXEC [Blah].[SendMessage];
GO
--read results from error log
--(might need to execute once or twice to get results - because service broker is asynchronous)
declare @sqlErrorLog table (LogDate datetime, ProcessInfo nvarchar(max), Text nvarchar(max));
INSERT INTO @sqlErrorLog EXEC xp_ReadErrorLog
SELECT * FROM @sqlErrorLog
WHERE LogDate >= DATEADD(SECOND, -15, GETDATE()) AND Text NOT LIKE 'CHECKDB%' AND Text NOT LIKE 'Starting up database ''upgrade%' AND Text NOT LIKE '%upgrade%information%' AND TEXT <> 'Error: 9001, Severity: 21, State: 1.'
ORDER BY LogDate
Run Code Online (Sandbox Code Playgroud)
Sol*_*zky 12
我需要避免将数据库标记为
Trustworthy
.
这当然是对 的正确态度TRUSTWORTHY
,是的,这是可能的。
那么为什么它在通过服务代理时在 msdb 中作为来宾执行呢?
我原本以为这个问题是使用 Impersonation 时跨数据库问题的典型原因:默认情况下,模拟数据库级别的主体(这是EXECUTE AS
子句,而不是语句可以做的)将被隔离到本地数据库.
然而,与 OP 的额外测试和讨论导致发现这种情况略有不同。似乎使用 Service Broker是极少数之一模块签名不能解决所有安全问题情况之一。这就是我曾在设置模块签署的典型实现,因为它没有工作思路。所以,我尝试了几件事,发现只有 SQLCLR 能够做到这一点。
然后最近我发现了一个相关的问题,sa 通过与 Service Broker 的同义词没有权限访问其他数据库,该问题引用了@Remus Rusanu 的一篇文章,其中 Remus 说这确实是可能的。考虑到 Remsus 的示例代码有效,我得出结论,我一定错过了一些小细节。而且,在查看细节时,我发现使用了一个违反直觉的选项:
更改程序以具有 EXECUTE AS 子句(否则代码签名基础结构不起作用)
通常模块签名允许您删除 EXECUTE AS
子句和语句,但这里是必需的。由于 Service Broker 通过EXECUTE AS USER =
语句在仅数据库安全上下文中工作,因此需要它。通过将EXECUTE AS
子句添加到CREATE PROCEDURE
语句中,创建了一个新的安全上下文,它可以访问服务器级和/或其他数据库,这是模块签名设置的其余部分处理的。
Sooooo,嗯,很抱歉把它弄对了,然后把它改成可行的,但由于缺少那一件而并不理想;-)。但是,我现在让它按照我最初所说的方式工作:-)。下面的第一组示例代码是可使用的纯 T-SQL 模块签名方法TRUSTWORTHY OFF
(现在我添加了缺少的WITH EXECUTE AS N'dbo'
)。我会将 SQLCLR 方法保留在底部,因为它确实有效并且可能更适合其他一些场景。
我希望通过签署程序......我可以获得跨数据库传输的权限
你可以。我认为我从未见过你的模块签名设置,但很可能你错过了我最初错过的一个小的、非典型的选项(这是让整个事情工作的关键)。
是否
execute as
否定通常通过证书用户关联的任何权限?
仅当它是EXECUTE AS USER
语句时(不是语句的EXECUTE AS
子句CREATE object
,也不是EXECUTE AS LOGIN
语句)。在那种情况下,安全上下文是并且只能是仅数据库,并且无法看到服务器级或其他数据库,即使模块签名就位。而且,幸运的是,这(即EXECUTE AS USER
语句)正是 Service Broker 为执行激活过程所做的。所以,是的,这就是阻止您最初尝试进行模块签名的原因。并且,修复它的技巧是简单地在访问另一个数据库的 procWITH EXECUTE AS N'dbo'
的CREATE PROCEDURE
语句中添加一个子句。你使用什么用户并不重要,但我发现dbo
使用简单化OWNER
如果所有者发生更改,则警告需要重新签署存储过程。当然,也可以更改数据库的所有者,所以我也希望我的选择会收到警告,但事实并非如此,所以我暂时选择忽略这种潜在的细微差别;-)。
主要设置
USE [master];
GO
IF (DB_ID(N'SendDbMailFromServiceBrokerQueue') IS NOT NULL)
BEGIN
RAISERROR(N'Dropping DB: [SendDbMailFromServiceBrokerQueue]...', 10, 1) WITH NOWAIT;
ALTER DATABASE [SendDbMailFromServiceBrokerQueue] SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE [SendDbMailFromServiceBrokerQueue] SET ONLINE WITH ROLLBACK IMMEDIATE;
DROP DATABASE [SendDbMailFromServiceBrokerQueue];
END
RAISERROR(N'Creating DB: [SendDbMailFromServiceBrokerQueue]...', 10, 1) WITH NOWAIT;
CREATE DATABASE [SendDbMailFromServiceBrokerQueue]
COLLATE Latin1_General_100_CI_AS_KS_SC
WITH DB_CHAINING OFF,
TRUSTWORTHY OFF;
ALTER DATABASE [SendDbMailFromServiceBrokerQueue]
SET RECOVERY SIMPLE,
PAGE_VERIFY CHECKSUM,
ENABLE_BROKER;
GO
-------------------------------------------------
USE [SendDbMailFromServiceBrokerQueue];
GO
CREATE SCHEMA [FunStuff] AUTHORIZATION [dbo];
GO
CREATE USER [BrokerUser] WITHOUT LOGIN WITH DEFAULT_SCHEMA=[dbo];
CREATE QUEUE [FunStuff].[SendingQueue];
CREATE SERVICE [//SendingService]
AUTHORIZATION [dbo]
ON QUEUE [FunStuff].[SendingQueue];
CREATE MESSAGE TYPE [//AuditMessage]
AUTHORIZATION [dbo]
VALIDATION = NONE;
CREATE CONTRACT [//AuditContract]
AUTHORIZATION [dbo]
([//AuditMessage] SENT BY INITIATOR);
GO
CREATE PROCEDURE [FunStuff].[SendMessage]
(
@Content NVARCHAR(MAX)
)
AS
SET NOCOUNT ON;
DECLARE @ConversationHandle UNIQUEIDENTIFIER;
BEGIN DIALOG CONVERSATION @ConversationHandle
FROM SERVICE [//SendingService]
TO SERVICE '//ReceivingService'
ON CONTRACT [//AuditContract]
WITH ENCRYPTION = OFF;
SEND ON CONVERSATION @ConversationHandle
MESSAGE TYPE [//AuditMessage]
(@Content) ;
END CONVERSATION @ConversationHandle ;
GO
---------------------------------------------------------------------------
GO
CREATE PROCEDURE [dbo].[EmailHandler]
(
@EmailSubject VARCHAR(255),
@EmailContent NVARCHAR(MAX)
)
WITH EXECUTE AS N'dbo' -- THIS IS REQUIRED (when used with Service Broker)!!!
AS
DECLARE @Recipients NVARCHAR(4000) = N'recipient@place.tld';
EXEC [msdb].[dbo].[sp_send_dbmail]
@profile_name = N'{my_pofile_name}',
@recipients = @Recipients,
@subject = @EmailSubject,
@body = @EmailContent,
@exclude_query_output = 1;
GO
CREATE PROCEDURE [FunStuff].[AuditMessageHandler]
(
@EmailSubject VARCHAR(255),
@EmailContent NVARCHAR(MAX)
)
AS
EXECUTE [dbo].[EmailHandler] @EmailSubject, @EmailContent;
GO
CREATE PROCEDURE [FunStuff].[AuditActivation]
AS
SET XACT_ABORT ON;
DECLARE @ConversationHandle UNIQUEIDENTIFIER,
@ConversationGroupID UNIQUEIDENTIFIER,
@MessageBody NVARCHAR(MAX),
@MessageTypeName NVARCHAR(256),
@RowsReceived INT;
WHILE (1 = 1)
BEGIN
WAITFOR(
GET CONVERSATION GROUP @ConversationGroupID
FROM [FunStuff].[ReceivingQueue]
), TIMEOUT 500;
IF (@ConversationGroupID IS NULL)
BEGIN
BREAK;
END;
WHILE (2 = 2)
BEGIN
BEGIN TRANSACTION;
RECEIVE TOP (1)
@ConversationHandle = [conversation_handle],
@MessageTypeName = [message_type_name],
@MessageBody = [message_body]
FROM [FunStuff].[ReceivingQueue]
WHERE CONVERSATION_GROUP_ID = @ConversationGroupID;
SET @RowsReceived = @@ROWCOUNT;
IF (@RowsReceived = 0)
BEGIN
COMMIT;
BREAK;
END;
IF (@MessageTypeName = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
BEGIN
END CONVERSATION @ConversationHandle;
END;
IF (@MessageTypeName = N'//AuditMessage')
BEGIN
EXEC [FunStuff].[AuditMessageHandler] N'Email From Broker test', @MessageBody;
END;
COMMIT TRANSACTION;
END; -- WHILE (2 = 2)
END; -- WHILE (1 = 1)
GO
GRANT EXECUTE ON [FunStuff].[AuditActivation] TO [BrokerUser];
GO
CREATE QUEUE [FunStuff].[ReceivingQueue]
WITH ACTIVATION (STATUS = ON,
PROCEDURE_NAME = [FunStuff].[AuditActivation],
MAX_QUEUE_READERS = 1,
EXECUTE AS N'BrokerUser'
);
CREATE SERVICE [//ReceivingService]
AUTHORIZATION [dbo]
ON QUEUE [FunStuff].[ReceivingQueue]
([//AuditContract]);
GO
Run Code Online (Sandbox Code Playgroud)
使此工作正常的模块签名步骤
USE [SendDbMailFromServiceBrokerQueue];
CREATE CERTIFICATE [Permission:SendDbMail]
ENCRYPTION BY PASSWORD = N'MyCertificate!MineMineMine!'
WITH SUBJECT = N'Grant permission to Send DB Mail',
EXPIRY_DATE = '2099-12-31';
-- Sign the Stored Procedure that accesses another DB
ADD SIGNATURE
TO [dbo].[EmailHandler]
BY CERTIFICATE [Permission:SendDbMail]
WITH PASSWORD = N'MyCertificate!MineMineMine!';
-- Copy the Certificate to [msdb]
DECLARE @PublicKey VARBINARY(MAX),
@SQL NVARCHAR(MAX);
SET @PublicKey = CERTENCODED(CERT_ID(N'Permission:SendDbMail'));
SET @SQL = N'
CREATE CERTIFICATE [Permission:SendDbMail]
FROM BINARY = ' + CONVERT(NVARCHAR(MAX), @PublicKey, 1) + N';';
PRINT @SQL; -- DEBUG
EXEC [msdb].[sys].[sp_executesql] @SQL;
-- Create the Certificate-based User in [msdb]
EXEC [msdb].[sys].[sp_executesql] N'CREATE USER [Permission:SendDbMail]
FROM CERTIFICATE [Permission:SendDbMail];
GRANT AUTHENTICATE TO [Permission:SendDbMail];
PRINT ''Adding Certificate-based User to DB Role [DatabaseMailUserRole]...'';
EXEC sp_addrolemember N''DatabaseMailUserRole'', N''Permission:SendDbMail'';
';
Run Code Online (Sandbox Code Playgroud)
测试
USE [SendDbMailFromServiceBrokerQueue];
-- execute statement below if there is an error and the queue is disabled:
-- ALTER QUEUE [FunStuff].[ReceivingQueue] WITH STATUS = ON, ACTIVATION (STATUS = ON);
EXEC [FunStuff].[SendMessage] @Content = N'Woo hoo!';
Run Code Online (Sandbox Code Playgroud)
我也能够使用 SQLCLR 让它工作(是的,没有启用TRUSTWORTHY
:-)。
SQLCLR C# 代码
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public class UserDefinedFunctions
{
[SqlProcedure()]
public static void ExecSendDbMail([SqlFacet(MaxSize = 255)] SqlString EmailSubject,
SqlString EmailContent)
{
using (SqlConnection _Connection = new
SqlConnection("Server=(local); Trusted_Connection=true; Enlist=false;"))
{
using (SqlCommand _Command = _Connection.CreateCommand())
{
_Command.CommandType = CommandType.StoredProcedure;
_Command.CommandText = @"dbo.sp_send_dbmail";
SqlParameter _ParamProfileName =
new SqlParameter("profile_name", SqlDbType.NVarChar, 128);
_ParamProfileName.Value = "{replace_with_your_profile_name}";
_Command.Parameters.Add(_ParamProfileName);
SqlParameter _ParamRecipients = new
SqlParameter("recipients", SqlDbType.VarChar, (int)SqlMetaData.Max);
_ParamRecipients.Value = "{replace_with_your_recipients}";
_Command.Parameters.Add(_ParamRecipients);
SqlParameter _ParamSubject =
new SqlParameter("subject", SqlDbType.NVarChar, 255);
_ParamSubject.Value = EmailSubject.Value;
_Command.Parameters.Add(_ParamSubject);
SqlParameter _ParamBody = new
SqlParameter("body", SqlDbType.NVarChar, (int)SqlMetaData.Max);
_ParamBody.Value = EmailContent.Value;
_Command.Parameters.Add(_ParamBody);
SqlParameter _ParamExcludeQueryOutput =
new SqlParameter("exclude_query_output", SqlDbType.Bit);
_ParamExcludeQueryOutput.Value = true;
_Command.Parameters.Add(_ParamExcludeQueryOutput);
_Connection.Open();
_Connection.ChangeDatabase("msdb");
_Command.ExecuteNonQuery();
}
}
return;
}
}
Run Code Online (Sandbox Code Playgroud)
设置
USE [master];
CREATE DATABASE [SendDbMailFromServiceBrokerQueue]
COLLATE Latin1_General_100_CI_AS_SC
WITH DB_CHAINING OFF,
TRUSTWORTHY OFF;
ALTER DATABASE [SendDbMailFromServiceBrokerQueue]
SET RECOVERY SIMPLE,
PAGE_VERIFY CHECKSUM,
ENABLE_BROKER;
GO
-- Create objects needed to allow for EXTERNAL_ACCESS without TRUSTWORTHY ON:
CREATE ASYMMETRIC KEY [Permission:SendDbMail$Key]
FROM EXECUTABLE FILE = N'C:\...\NoTrustworthy.dll';
CREATE LOGIN [Permission:SendDbMail$Login]
FROM ASYMMETRIC KEY [Permission:SendDbMail$Key];
GRANT EXTERNAL ACCESS ASSEMBLY TO [Permission:SendDbMail$Login];
GO
-------------------------------------------------
USE [SendDbMailFromServiceBrokerQueue];
GO
CREATE ASSEMBLY [NoTrustworthy]
AUTHORIZATION [dbo]
FROM N'C:\...\NoTrustworthy.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS;
GO
CREATE PROCEDURE [dbo].[ExecSendDbMail]
(
@EmailSubject NVARCHAR (255),
@EmailContent NVARCHAR (MAX)
)
AS EXTERNAL NAME [NoTrustworthy].[UserDefinedFunctions].[ExecSendDbMail];
GO
CREATE SCHEMA [FunStuff] AUTHORIZATION [dbo];
GO
CREATE USER [BrokerUser] WITHOUT LOGIN WITH DEFAULT_SCHEMA=[dbo];
CREATE QUEUE [FunStuff].[SendingQueue];
CREATE SERVICE [//SendingService]
AUTHORIZATION [dbo]
ON QUEUE [FunStuff].[SendingQueue];
CREATE MESSAGE TYPE [//AuditMessage]
AUTHORIZATION [dbo]
VALIDATION = NONE;
CREATE CONTRACT [//AuditContract]
AUTHORIZATION [dbo]
([//AuditMessage] SENT BY INITIATOR);
GO
CREATE PROCEDURE [FunStuff].[SendMessage]
(
@Content NVARCHAR(MAX)
)
AS
SET NOCOUNT ON;
DECLARE @ConversationHandle UNIQUEIDENTIFIER;
BEGIN DIALOG CONVERSATION @ConversationHandle
FROM SERVICE [//SendingService]
TO SERVICE '//ReceivingService'
ON CONTRACT [//AuditContract]
WITH ENCRYPTION = OFF;
SEND ON CONVERSATION @ConversationHandle
MESSAGE TYPE [//AuditMessage]
(@Content) ;
END CONVERSATION @ConversationHandle ;
GO
---------------------------------------------------------------------------
CREATE PROCEDURE [dbo].[EmailHandler]
(
@EmailSubject VARCHAR(255),
@EmailContent NVARCHAR(MAX)
)
AS
-- other logic
EXEC [dbo].[ExecSendDbMail] @EmailSubject, @EmailContent;
GO
GO
CREATE PROCEDURE [FunStuff].[AuditMessageHandler]
(
@EmailSubject VARCHAR(255),
@EmailContent NVARCHAR(MAX)
)
AS
EXECUTE [dbo].[EmailHandler] @EmailSubject, @EmailContent;
GO
CREATE PROCEDURE [FunStuff].[AuditActivation]
AS
SET XACT_ABORT ON;
DECLARE @ConversationHandle UNIQUEIDENTIFIER,
@ConversationGroupID UNIQUEIDENTIFIER,
@MessageBody NVARCHAR(MAX),
@MessageTypeName NVARCHAR(256),
@RowsReceived INT;
WHILE (1 = 1)
BEGIN
WAITFOR(
GET CONVERSATION GROUP @ConversationGroupID
FROM [FunStuff].[ReceivingQueue]
), TIMEOUT 500;
IF (@ConversationGroupID IS NULL)
BEGIN
BREAK;
END;
WHILE (2 = 2)
BEGIN
BEGIN TRANSACTION;
PRINT 'Get Message';
RECEIVE TOP (1)
@ConversationHandle = [conversation_handle],
@MessageTypeName = [message_type_name],
@MessageBody = [message_body]
FROM [FunStuff].[ReceivingQueue]
WHERE CONVERSATION_GROUP_ID = @ConversationGroupID;
SET @RowsReceived = @@ROWCOUNT;
IF (@RowsReceived = 0)
BEGIN
COMMIT;
BREAK;
END;
IF (@MessageTypeName =
N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
BEGIN
END CONVERSATION @ConversationHandle;
END;
IF (@MessageTypeName = N'//AuditMessage')
BEGIN
EXEC [FunStuff].[AuditMessageHandler]
N'Email From Broker test', @MessageBody;
END;
COMMIT TRANSACTION;
END; -- WHILE (2 = 2)
END; -- WHILE (1 = 1)
GO
GRANT EXECUTE ON [FunStuff].[AuditActivation] TO [BrokerUser];
GO
CREATE QUEUE [FunStuff].[ReceivingQueue]
WITH ACTIVATION (STATUS = ON,
PROCEDURE_NAME = [FunStuff].[AuditActivation],
MAX_QUEUE_READERS = 1,
EXECUTE AS N'BrokerUser'
);
CREATE SERVICE [//ReceivingService]
AUTHORIZATION [dbo]
ON QUEUE [FunStuff].[ReceivingQueue]
([//AuditContract]);
GO
---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
测试
USE [SendDbMailFromServiceBrokerQueue];
-- execute statement below if there is an error and the queue is disabled:
-- ALTER QUEUE [FunStuff].[ReceivingQueue] WITH STATUS = ON, ACTIVATION (STATUS = ON);
EXEC [FunStuff].[SendMessage] @Content = N'try me!';
Run Code Online (Sandbox Code Playgroud)
清理
IF (DB_ID(N'SendDbMailFromServiceBrokerQueue') IS NOT NULL)
BEGIN
RAISERROR(N'Dropping DB: [SendDbMailFromServiceBrokerQueue]...', 10, 1) WITH NOWAIT;
ALTER DATABASE [SendDbMailFromServiceBrokerQueue] SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE [SendDbMailFromServiceBrokerQueue] SET ONLINE WITH ROLLBACK IMMEDIATE;
DROP DATABASE [SendDbMailFromServiceBrokerQueue];
END
DROP LOGIN [Permission:SendDbMail$Login];
DROP ASYMMETRIC KEY [Permission:SendDbMail$Key];
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2103 次 |
最近记录: |