如何在使用托管 ODP.NET 时从 C# 查询 LDAP 以解析 Oracle TNS 主机名

本文关键字:LDAP 查询 Oracle 主机 TNS 时从 NET ODP | 更新日期: 2023-09-27 17:56:16

继我之前的问题之后,我在 Oracle 论坛的帮助下设法回答了自己,我现在有另一个问题,这是前一个问题的后续问题(提供背景)。

我希望直接从我的 C# 代码查询 LDAP,以执行 Oracle TNS 主机名的 LDAP 查找,以获取连接字符串。这通常存储在tnsnames.ora中,我的组织使用LDAP(通过ldap.ora)使用Active Directory从LDAP服务器解析主机名。

但是,我在我的 C# 应用程序中使用了 ODP.NET 托管驱动程序测试版 (Oracle.ManagedDataAccess.dll),该应用程序不支持 LDAP,如我之前提到的 Oracle 论坛回复所指出的发行说明中所述。这就是为什么我希望直接从 C# 查询 LDAP。

我在这里找到了一种使用 DirectoryEntryDirectorySearcher 来做到这一点的方法,但我不知道该将什么作为参数来DirectorySearcher.我可以访问以下格式的ldap.ora

# LDAP。奥拉配置
# 由 Oracle 配置工具生成。
DEFAULT_ADMIN_CONTEXT = "dc=xx,dc=mycompany,dc=com"
DIRECTORY_SERVERS = (ldap_server1.mycompany.com:389:636,ldap_server2.mycompany.com:389:636, ...) DIRECTORY_SERVER_TYPE = OID

但是,如何将其映射到在 C# 代码中设置 LDAP 查询?

如何在使用托管 ODP.NET 时从 C# 查询 LDAP 以解析 Oracle TNS 主机名

继我在接受的答案中的第二条评论之外,这是执行 LDAP 查找的代码,它改进了我在这里找到的原始版本。它还处理 ldap.ora 文件中包含多个分隔端口号的服务器列表。

private static string ResolveServiceNameLdap(string serviceName)
{
    string tnsAdminPath = Path.Combine(@"C:'Apps'oracle'network'admin", "ldap.ora");
    string connectionString = string.Empty;
    // ldap.ora can contain many LDAP servers
    IEnumerable<string> directoryServers = null;
    if (File.Exists(tnsAdminPath))
    {
        string defaultAdminContext = string.Empty;
        using (var sr = File.OpenText(tnsAdminPath))
        {
            string line;
            while ((line = sr.ReadLine()) != null)
            {
                // Ignore commetns
                if (line.StartsWith("#"))
                {
                    continue;
                }
                // Ignore empty lines
                if (line == string.Empty)
                {
                    continue;
                }
                // If line starts with DEFAULT_ADMIN_CONTEXT then get its value
                if (line.StartsWith("DEFAULT_ADMIN_CONTEXT"))
                {
                    defaultAdminContext = line.Substring(line.IndexOf('=') + 1).Trim(new[] {''"', ' '});
                }
                // If line starts with DIRECTORY_SERVERS then get its value
                if (line.StartsWith("DIRECTORY_SERVERS"))
                {
                    string[] serversPorts = line.Substring(line.IndexOf('=') + 1).Trim(new[] {'(', ')', ' '}).Split(',');
                    directoryServers = serversPorts.SelectMany(x =>
                    {
                        // If the server includes multiple port numbers, this needs to be handled
                        string[] serverPorts = x.Split(':');
                        if (serverPorts.Count() > 1)
                        {
                            return serverPorts.Skip(1).Select(y => string.Format("{0}:{1}", serverPorts.First(), y));
                        }
                        return new[] {x};
                    });
                }
            }
        }
        // Iterate through each LDAP server, and try to connect
        foreach (string directoryServer in directoryServers)
        {
            // Try to connect to LDAP server with using default admin contact
            try
            {
                var directoryEntry = new DirectoryEntry("LDAP://" + directoryServer + "/" + defaultAdminContext, null, null, AuthenticationTypes.Anonymous);
                var directorySearcher = new DirectorySearcher(directoryEntry, "(&(objectclass=orclNetService)(cn=" + serviceName + "))", new[] { "orclnetdescstring" }, SearchScope.Subtree);
                SearchResult searchResult = directorySearcher.FindOne();
                var value = searchResult.Properties["orclnetdescstring"][0] as byte[];
                if (value != null)
                {
                    connectionString = Encoding.Default.GetString(value);
                }
                // If the connection was successful, then not necessary to try other LDAP servers
                break;
            }
            catch
            {
                // If the connection to LDAP server not successful, try to connect to the next LDAP server
                continue;
            }
        }
        // If casting was not successful, or not found any TNS value, then result is an error message
        if (string.IsNullOrEmpty(connectionString))
        {
            connectionString = "TNS value not found in LDAP";
        }
    }
    else
    {
        // If ldap.ora doesn't exist, then return error message
        connectionString = "ldap.ora not found";
    }
    return connectionString;
}

根据我在使用 OpenLDAP 的 Oracle 数据库名称解析中发现的内容来看,代码应如下所示:

string directoryServer = "ldap_server1.mycompany.com:389";
string defaultAdminContext = "dc=xx,dc=mycompany,dc=com";
string oracleHostEntryPath = string.Format("LDAP://{0}/cn=OracleContext,{1}", directoryServer, defaultAdminContext);
var directoryEntry = new DirectoryEntry(oracleHostEntryPath) {AuthenticationType = AuthenticationTypes.None};
var directorySearcher = new DirectorySearcher(directoryEntry, "(&(objectclass=orclNetService)(cn=ABCDEFG1))", new[] { "orclnetdescstring" }, SearchScope.Subtree);
string oracleNetDescription = Encoding.Default.GetString(des.FindOne().Properties["orclnetdescstring"][0] as byte[]);

要帮助您入门,请尝试:

using System.DirectoryServices;
...
DirectoryEntry de = new DirectoryEntry();
de.Username = "username@mysite.com";
de.Password = "password";
de.Path = "LDAP://DC=mysite,DC=com";
de.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher des = new DirectorySearcher(de);

现在你应该能够使用这里描述的DirectorySearcher(filter,findone等)点击ldap

这里有一个基于其他答案的更完整的示例:

using System;
using System.Data;
using System.DirectoryServices;
using System.Text;
using Oracle.ManagedDataAccess.Client;
namespace BAT
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string directoryServer = "myServer:389";
            string defaultAdminContext = "cn=OracleContext,dc=world";
            string serviceName = "MYDB";
            string userId = "MYUSER";
            string password = "MYPW";
            using (IDbConnection connection = GetConnection(directoryServer, defaultAdminContext, serviceName, userId,
                    password))
            {
                connection.Open();
                connection.Close();
            }
        }
        private static IDbConnection GetConnection(string directoryServer, string defaultAdminContext,
            string serviceName, string userId, string password)
        {
            string descriptor = ConnectionDescriptor(directoryServer, defaultAdminContext, serviceName);
            // Connect to Oracle
            string connectionString = $"user id={userId};password={password};data source={descriptor}";
            OracleConnection con = new OracleConnection(connectionString);
            return con;
        } 
        private static string ConnectionDescriptor(string directoryServer, string defaultAdminContext,
            string serviceName)
        {
            string ldapAdress = $"LDAP://{directoryServer}/{defaultAdminContext}";
            string query = $"(&(objectclass=orclNetService)(cn={serviceName}))";
            string orclnetdescstring = "orclnetdescstring";
            DirectoryEntry directoryEntry = new DirectoryEntry(ldapAdress, null, null, AuthenticationTypes.Anonymous);
            DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry, query, new[] { orclnetdescstring },
                SearchScope.Subtree);
            SearchResult searchResult = directorySearcher.FindOne();
            byte[] value = searchResult.Properties[orclnetdescstring][0] as byte[];
            if (value != null)
            {
                string descriptor = Encoding.Default.GetString(value);
                return descriptor;
            }
            throw new Exception("Error querying LDAP");
        }
    }
}