SQL Server – Active Directory 交互






1.54/5 (6投票s)
描述 SQL Server 2000/2005 与 Windows 2000 或 2003 服务器上的 Active Directory 的交互
1 引言
Active Directory 是基于 LDAP 的目录,首次引入于 Windows 2000。它提供了分层、基于容器的对象组织,这些对象代表各种域实体,例如用户、组、打印机或计算机(以及它们固有的属性)。其内部复制机制和多主控能力使其具有可伸缩性和健壮性。它还具有每个目录都应提供的若干功能,例如广泛的搜索功能和可扩展性。
SQL Server 和 Active Directory 可以通过两种方式进行交互
- 将 SQL Server 注册为 Active Directory 的一部分
- 使用 SQL Server 功能访问 Active Directory 信息
本文档解释了使用 SQL Server 功能访问 Active Directory 信息的方式
1.1 先决条件
从 SQL Server 访问 Active Directory 的先决条件如下
- Windows 2000 或 2003 服务器
- Active Directory
- SQL Server 2000/2005
2 从 SQL Server 2000 访问 Active Directory
可以通过链接服务器或 OpenRowSet 来从 SQL Server 访问 Active Directory。
2.1 使用链接服务器
Microsoft 的对象链接和嵌入数据库 (OLE DB) 提供程序可以创建到非 SQL Server 数据源以及其他 SQL 服务器的连接。OLE DB 还可以连接到某些非关系型数据源。OLE DB 提供程序使非关系型数据显示为关系型数据。因此,SQL Server 还可以查询域的 Active Directory 内容,Active Directory 的结构类似于树,而不是表。以下是访问 Active Directory 的步骤
- 创建链接服务器定义
- 查找要查询的目录元素的 LDAP 名称
- 编写 SELECT 语句以获取它们
安全问题
创建链接服务器后,它会自动设置为使用自命名;也就是说,将用户的 Windows 登录凭据传递给 Active Directory,以查看他们是否有权使用它。Active Directory 中的每个对象都带有一个安全描述符,该描述符指示允许每个用户执行哪些操作。如果尝试从目录中进行选择的用户没有正确的权限,则会拒绝访问。
2.1.1 创建链接服务器
有两种方法可以在 SQL Server 中创建到 Active Directory 的链接服务器。
2.1.1.1 使用系统存储过程
系统存储过程 sp_addlinkedserver 可用于创建链接服务器。其语法为:
EXEC sp_addlinkedserver 'ADSI', 'Active Directory Services 2.5', 'ADSDSOObject', 'adsdatasource'
第一个参数 ADSI 是您在查询链接服务器时使用的名称。Active Directory Services 2.5 是产品名称,ADSDSOObject 是提供程序,adsdatasource 是内置数据源名称。
2.1.1.2 使用企业管理器
以下是使用企业管理器设置到 Active Directory 的链接服务器的步骤
- 展开“安全”容器
- 右键单击“链接服务器”
- 选择“新建链接服务器”
轻量级目录访问协议 (LDAP) 是用于 Active Directory 等目录服务的查询语言。在 Active Directory 中看到的每个数据元素都有两个名称:属性页(如 Active Directory 用户和计算机)上显示的显示名称,以及内部 LDAP 名称。您需要使用后者来查询 Active Directory。以下是查找相同的方法
- 定位内部 LDAP 名称的最简单方法是使用名为 Active Directory Schema 的 Microsoft 管理控制台管理单元。它默认未在 Windows 2000 上安装,尽管其 DLL 存在。要注册此 DLL(从而使其在“添加管理单元”对话框中可见),请在您拥有本地管理员权限的数据库服务器上打开控制台窗口,然后键入
Regsvr32 schmmgmt.dll
将显示消息:“DllRegisterServer in schmmgmt.dll succeeded.”。
- 通过在“运行”命令的文本框中键入命令 mmc,打开一个新的、空的 MMC 控制台。
- 在“控制台”菜单中,选择“添加/删除管理单元”和“添加”按钮。
- 在列表中,单击“Active Directory Schema”并单击“确定”。
- 导航窗格中将显示一个分层(树状)视图。通过打开各个级别,可以浏览 Active Directory 的结构或模式。
- 如果展开“类”容器并单击“用户”类,右侧的显示面板将充满构成用户定义的所有元素。列出的名称是您在 SQL 中查询 Active Directory 时需要使用的 LDAP 名称。每个元素还有说明。例如,如果要显示用户的名字和姓氏,您将在列表下找到 LDAP 名称 givenName 和 sn(分别代表 surname)。
提示:如果您无法使用 Active Directory Schema 管理单元,也并非束手无策。MSDN 网站上的文章 “用户对象用户界面映射” 中记录了许多这些元素。
2.1.3 查询 Active Directory
不能使用通常的四部分命名约定(用于数据库的链接服务器,例如 OTHERSERV.MyDatabase.dbo.Employees)。而是使用 OPENQUERY 函数编写传递查询。此外,只能发送 SELECT 语句;不能通过 ADSI 发送 INSERT、UPDATE 和 DELETE 语句。
例如,要获取用户的名字和姓氏,请编写以下内容
SELECT *
FROM OPENQUERY (ADSI,
'SELECT givenName, sn FROM LDAP:// DC=yourcompany,DC=com'
请注意,此查询仅使用 SELECT * 从 OPENQUERY 函数返回所有列,使用它们的原始名称。您还可以为返回的列设置别名以提供更易读的名称。LDAP 查询本身可以是上面的形式(称为无服务器连接),也可以命名要连接的特定服务器。
LDAP://LAXPDC01/ DC=yourcompany,DC=com
或者,它可以使用域让任何域控制器响应
LDAP://MYCORP/ DC=yourcompany,DC=com
最后一个斜杠后面的部分称为要搜索的 Active Directory 对象的相对区分名 (RDN)。它从右到左读取,因此其他限定符会添加到左侧。例如,如果要列出 Finance 组织单位 (OU) 中的用户,您将这样写
LDAP://MYCORP/ OU=Finance,DC=yourcompany,DC=com
也可以直接从 SQL Server 2000 访问 Active Directory,而无需创建链接服务器。
SELECT *
FROM OPENROWSET('AdsDsoObject',
'User ID= UserID; Password= Pwd; ADSI Flag=0x11;Page Size=10000',
'SELECT givenName,sn
FROM ''LDAP://addevdc01/OU=Users,OU=PerotSystems,DC=devtenethealth,DC=net''')
where sn like 'user1'
2.1.4 示例应用程序
假设您使用包含员工信息的表的第三方人力资源软件。由于这是一个购买的软件包,您无法更改 Employee 表的布局,并且它没有用于员工网页的列。然而,您的许多员工都开始了自己的 Weblogs(博客),您希望能够创建一个报告,其中包含 HR 包中的一些信息,以及拥有博客的员工的 Web 地址。
Active Directory 中的用户定义包含一个用于此的字段。在 Active Directory 用户和计算机中,它位于“常规”选项卡上,显示名称为网页。在 Active Directory Schema 管理单元中查找,您会发现此属性的 LDAP 名称是 wWWHomePage。更新 Active Directory 中的用户记录后,您可以创建一个 SELECT 语句来查询它们,并将其存储为视图。
CREATE VIEW bloggers_view
AS
SELECT givenName AS FirstName, sn AS LastName, wWWHomePage AS Weblog
FROM OPENQUERY (ADSI,
'SELECT givenName,sn,wWWHomePage FROM LDAP:// DC=yourcompany,DC=com')
WHERE wWWHomePage IS NOT NULL
现在您可以将 Active Directory 数据与 Employee 表联接
SELECT e.FirstName, e.LastName, e.StartDate, bv.Weblog
FROM Employee AS e LEFT OUTER JOIN blogger_view AS bv
ON e.LastName = bv.Lastname AND e.FirstName = bv.LastName
2.2 使用 OpenROWSET(无需链接服务器)
我们也可以在不使用链接服务器的情况下访问 Active Directory。为此,我们必须使用 OpenRowSet。
OpenRowSet 包含访问 OLE DB 数据源的远程数据所需的所有连接信息。此方法是访问链接服务器中表的替代方法,是一种使用 OLE DB 连接和访问远程数据的单次、临时方法。
OPENROWSET ( 'provider_name' , { 'datasource' ; 'user_id' ; 'password' | 'provider_string' } , { [ catalog. ] [ schema. ] object | 'query' } )
OPENROWSET 的权限由传递给 OLE DB 提供程序的用户名权限决定。
SELECT * FROM OPENROWSET( 'AdsDsoObject' , 'User ID=user1;Password=pwd1;ADSI Flag=0x11;Page Size=10000', 'SELECT givenName,sn FROM ''LDAP://addevdc01/OU=Users,OU=account1,DC=dev,DC=net''') where sn like 'Mike'
3 从 SQL Server 2005 访问 Active Directory
可以通过以下方式从 SQL Server 2005 访问 Active Directory
- 链接服务器
- OpenRowSet
- 托管存储过程
3.1 使用托管存储过程
SQL Server 2005 与 .NET CLR 的集成使得开发人员可以使用托管语言(如 VB.NET 或 C#)编写存储过程、触发器、用户定义函数以及创建其他数据库对象。
使用 .Net 代码从 SQL Server 访问 Active Directory 需要执行的任务顺序如下
- 创建一个 .NET 类,并在该类中实现存储过程的功能
- 将该类编译为 .NET 程序集
- 使用 Create Assembly 语句在 SQL Server 中注册该程序集
- 创建存储过程定义。作为此过程的一部分,您还会在存储过程与程序集中的实际方法之间建立关联。
完成这些步骤后,即可配置存储过程,并且可以像执行其他任何存储过程一样执行它们。
3.1.1 实现基于 .Net 类的存储过程
有许多方法可以通过 .Net 以编程方式访问 Active Directory,例如 COM 对象、ADSI 等。最简单的方法是使用 .NET Framework 中的 System.DirectoryServices 命名空间中的类,它建立在 Active Directory 服务接口 (ADSI) API 之上。
System.DirectoryServices 命名空间提供了两个重要类:DirectoryEntry 和 DirectorySearcher,用于处理 Active Directory。DirectoryEntry 类表示 Active Directory 中的资源,DirectorySearcher 类用于查询 Active Directory。
上述类中的大多数方法都需要相同的参数。
- friendlyDomainName:非限定域名(例如 contoso,而不是 contoso.com)
- ldapDomain:完全限定域名,例如 contoso.com 或 dc=contoso,dc=com
- objectPath:对象的完全限定路径:CN=user,OU=USERS,DC=contoso,DC=com(与 objectDn 相同)
- objectDn:对象的区分名:CN=group,OU=GROUPS,DC=contoso,DC=com
- userDn:用户的区分名:CN=user,OU=USERS,DC=contoso,DC=com
- groupDn:组的区分名:CN=group,OU=GROUPS,DC=contoso,DC=com
- 完全在公共语言运行时参数内设计。System.DirectoryServices 利用公共语言运行时特性,如垃圾回收、自定义索引器和字典(哈希表)。它还提供其他公共语言运行时特性,如自动内存管理、高效部署、面向对象的框架、基于证据的安全性和异常处理。
- 易于使用。虽然 ADSI 脚本对于许多任务来说都很有效,但 ADSI 的 C++ 应用程序有时很难开发。System.DirectoryServices 实现了一些基本的 ADSI 任务,以实现更有效和更有效的应用程序开发。
Using System.Data
Using System.Data.Sql
Using System.Data.SqlServer
Using System.DirectoryServices
Public Class ActiveDirectoryFunctions
{
Public AuthenticateUser( String _userName, String _password, String _domain)
{
Bool authentic = false;
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain,userName, password);
object nativeObject = entry.NativeObject;
authentic = true;
SqlPipe sp = SqlContext.GetPipe();
Sp.send(authentic.toString());
}
catch (DirectoryServicesCOMException) { }
return;
}
}
有关几乎所有 Active Directory 操作的代码片段可以在以下位置找到:everythingInAD.asp
要在 SQL Server 中执行 .NET 代码,您需要引用 System.Data.Sql 和 System.Data.SqlServer 命名空间。AddressType 类包含一个名为 GetAddressTypeDetails 的方法,该方法使用作为参数传递的 ID 从随 SQL Server 2005 一起提供的 AdventureWorks 数据库中的 AddressType 表检索一行。该代码通过调用 SqlContext 类的 GetPipe 方法来获取 SqlPipe 对象的引用。然后,您可以使用此 SqlPipe 引用通过 SqlPipe.Send 方法将表格式结果和消息返回给客户端。通过调用 Send 方法的各种重载,您可以将数据通过管道传输到调用应用程序。Send 方法的一些重载版本是:
- Send(ISqlDataReader)—允许我们以 SqlDataReader 对象的形式发送表格式结果
- Send(ISqlDataRecord)—允许我们以 SqlDataRecord 对象的形式发送结果
- Send(ISqlError)—使我们能够以 SqlError 对象的形式发送错误信息。*
- Send(msg As String)—使用此方法,我们可以将消息发送到调用应用程序。
3.1.2 在 SQL Server 2005 中注册 .Net 程序集
托管代码的部署单元称为程序集。程序集被打包为 DLL 或可执行文件 (EXE) 文件。可执行文件可以独立运行,而 DLL 必须托管在现有应用程序中。SQL Server 可以加载和托管托管 DLL 程序集。要将程序集加载到 SQL Server 中,您需要使用 Create Assembly 语句。
CREATE ASSEMBLY AddressType
FROM 'C:\Program Files\Microsoft SQL Server\MSSQL.1\' +
'MSSQL\Binn\Test\CLRProcedures\CSharp\ ActiveDirectoryFunctions.dll'
在 SQL Server Workbench 中执行上述语句时,它会将程序集加载到 SQL Server 中。FROM 子句指定要加载的程序集的路径名。此路径可以是 UNC 路径,也可以是本地到计算机的物理文件路径。上述语句会将程序集注册到 SQL Server(程序集名称在一个数据库中必须是唯一的)。运行 Create Assembly 语句会导致 SQL Server 加载程序集的副本。之后,如果您想更改程序集代码,需要先删除程序集,然后更改代码,重新编译程序集,最后,再次使用 SQL Server 注册程序集。要从 SQL Server 中删除程序集,请使用 Drop Assembly 语句。例如,要删除先前创建的程序集,请使用以下命令。
DROP ASSEMBLY ActiveDirectoryFunctions
3.1.3 创建存储过程定义
将程序集加载到 SQL Server 后,您需要将存储过程与程序集中的类的特定方法关联起来。您使用 Create Procedure 语句创建存储过程。SQL Server 2005 支持一个名为 External Name 的新子句,它允许您引用已注册程序集中的方法(外部方法)。引用外部方法会将存储过程与程序集中的该方法进行挂钩。
CREATE PROCEDURE [dbo].[ AuthenticateUser]
@userId varchar(30),
@password varchar(30),
@domain varchar(30)
AS
EXTERNAL NAME
[ActiveDirectoryFunctions]:[ ActiveDirectoryFunctions]::
[AuthenticateUser]
前面的代码使用了 ActiveDirectoryFunctions 类中前面显示的 AuthenticateUser 方法。External Name 子句使用以下语法。
[程序集名称]:[类名称]::[方法名称]
创建存储过程后,您可以使用以下语句进行测试
exec AuthenticateUser 'User1', 'Password1',' addevdc01/OU=Users,OU=Account1,DC=devDomain,DC=net'