You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure LDAP认证在云服务器及其他设备上连接被拒求助

问题:Azure LDAP认证在本地正常,部署到DigitalOcean后连接被拒

我是Azure LDAP新手,正在开发Next.js站点,用户可通过Microsoft Entra ID(Azure Active Directory)的邮箱密码完成认证。该功能在本地http://localhost:3000可正常登录,但部署到DigitalOcean云主机后出现连接被拒错误,在其他笔记本使用时也有同样问题。请问我是否遗漏了配置,或是需要为云主机生成证书?

以下是用于连接LDAP的代码:

import { NextResponse } from "next/server";
import ldap from "ldapjs";
import fs from "fs";
import path from "path";
import mysql from "mysql2/promise";
import jwt from "jsonwebtoken";

export async function POST(req) {

    if (req.method === 'POST') {
        const { username, email, password, token = '0' } = await req.json();

        var db = null;
        let ldapSettings = null;
        try {
            db = await mysql.createConnection({
                host: process.env.DB_HOST,
                user: process.env.DB_USER,
                password: process.env.DB_PASSWORD,
                database: process.env.DB_DATABASE,
            })

            const [ldapSettingsData] = await db.execute(`SELECT * from ldap_settings WHERE login_token='${token}'`);
            if (ldapSettingsData.length == 0) {
                return NextResponse.json({ success: false, "error": "LDAP Settings not found!" }, { status: 400 })
            }

            if (!ldapSettingsData[0]['ldap_enabled']) {
                return NextResponse.json({ success: false, "error": "LDAP not available, Contact admin for LDAP Login!" }, { status: 400 })
            }

            ldapSettings = ldapSettingsData;
        } catch (e) {
            return NextResponse.json({ success: false, "error": "Server error occurred!" }, { status: 500 })
        } finally {
            if (db) {
                db.destroy();
            }
        }

        let ldapCert = null;
        try {
            ldapCert = path.join(process.cwd(), 'public', "certificates", (ldapSettings?.[0]?.["ldap_cert_file"] ?? ""));
            ldapCert = fs.readFileSync(ldapCert);
        } catch (e) {
            return NextResponse.json({ error: "Certificate File not found!" }, { status: 404 });
        }

        const client = ldap.createClient({
            url: ldapSettings[0]['ldap_server'] + ":" + (ldapSettings[0]['use_tls'] ? "389" : "636"),
            tlsOptions: {
                ca: [ldapCert],
                rejectUnauthorized: (ldapSettings?.[0]?.ldap_ssl_cert_verif != null && ldapSettings[0]['ldap_ssl_cert_verif'] ? false : true)
            },
            reconnect: true,
        });

        client.on('error', (err) => {
            console.error("LDAP connection error:", err.message);
            if (err.code) {
                console.error("Error code:", err.code);
            }
        });

        client.on('connect', () => {
            console.log("Successfully connected to the LDAP server.");
        });

        return new Promise((resolve) => {
            let response = null;

            client.bind(("CN=" + username + `,${ldapSettings[0]['is_azure_ad'] ? ldapSettings[0]['base_bind_dn'].replace(/ou=|OU=/g, "ou=AADDC ") : ldapSettings[0]['base_bind_dn']}`), password, (error) => {
                if (error) {
                    response = NextResponse.json({ success: false, error: "Invalid LDAP Credentials or LDAP Error Occured!" }, { status: 401 });
                    client.unbind();
                    resolve(response);
                    return;
                } else {
                    const opts = {
                        filter: ldapSettings[0]['ldap_filter'].replace(/{email}/g, email).replace(/{username}/g, username),
                        scope: 'sub',
                        attributes: [],
                    }

                    let user_exists = false;
                    client.search("CN=" + username + `,${ldapSettings[0]['is_azure_ad'] ? ldapSettings[0]['base_bind_dn'].replace(/ou=|OU=/g, "ou=AADDC ") : ldapSettings[0]['base_bind_dn']}`, opts, (err, res) => {
                        if (err) {
                            response = NextResponse.json({ success: false, error: "Search Query Error" }, { status: 401 });
                            client.unbind();
                            resolve(response);
                            return;
                        }

                        res.on('searchEntry', (entry) => {
                            user_exists = true;
                        });

                        res.on('error', (err) => {
                            console.log(err);
                            response = NextResponse.json({ success: false, error: "User finding error!" }, { status: 401 });
                            client.unbind();
                            resolve(response);
                        });

                        res.on('end', async (result) => {
                            if (user_exists) {
                                let db = null;
                                try {
                                    db = await mysql.createConnection({
                                        host: process.env.DB_HOST,
                                        user: process.env.DB_USER,
                                        password: process.env.DB_PASSWORD,
                                        database: process.env.DB_DATABASE,
                                        port: process.env.DB_PORT
                                    });

                                    const [rows] = await db.execute('SELECT id, source, admin, name from users WHERE email=? and source=?', [email, "ldap"]);

                                    let user_id = '';
                                    let adminId = ldapSettings[0]['admin_id'];
                                    let user_dp = '';
                                    let user_name = '';

                                    if (rows.length === 0) {
                                        await db.execute("INSERT INTO users SET admin=?, name=?, email=?, source=?, role=?", [adminId, username, email, 'ldap', 'employee']).then(([result]) => {
                                            user_id = result.insertId;
                                        });
                                        user_name = username;
                                    } else {
                                        user_id = rows[0]['id'];
                                        adminId = rows[0]['admin'];
                                        user_dp = (rows[0]?.dp ?? "");
                                        user_name = (rows[0]?.name ?? "");
                                    }

                                    const SECRET_KEY = process.env.JWT_SECRET || 'your_secret_key';
                                    const token = jwt.sign(
                                        { id: user_id, admin_id: adminId, name: user_name, email: email, source: "LDAP", dp: (user_dp ?? ''), role: 'employee'},
                                        SECRET_KEY,
                                        { expiresIn: process.env.JWT_TOKEN_EXPIRES || '24h' }
                                    );

                                    const response = NextResponse.json({
                                        message: 'Authenticated successfully!',
                                    }, { status: 200 });

                                    response.cookies.set('token', token, {
                                        httpOnly: true,
                                        maxAge: process.env.COOKIES_EXPIRES_SECONDS || (24 * 60 * 60),
                                        path: '/',
                                    });

                                    client.unbind();
                                    resolve(response);
                                } catch (error) {
                                    console.error('Error logging in:', error);
                                    response = NextResponse.json({ error: 'Error Occured within Server!' }, { status: 500 });
                                    client.unbind();
                                    resolve(response);
                                } finally {
                                    if (db) {
                                        db.destroy();
                                    }
                                }
                            } else {
                                response = NextResponse.json({ success: false, error: "User not found!" }, { status: 401 });
                            }
                            client.unbind();
                            resolve(response);
                        });
                    });
                }
            });
        });
    } else {
        return NextResponse.json({ error: 'Only POST requests are allowed' }, { status: 405 });
    }
}

可能的原因及解决步骤

1. 网络/防火墙限制

  • DigitalOcean云主机的Cloud Firewall需要放行对应LDAP端口:
    • LDAPS(SSL)模式放行636端口(TCP)
    • STARTTLS模式放行389端口(TCP)
  • 检查Azure端配置:将DigitalOcean主机的公网IP添加到Azure AD Domain Services(AAD DS)的允许访问列表,Azure默认会限制非信任IP的LDAP连接。

2. 证书配置问题

  • 确认DigitalOcean主机上的证书文件存在于public/certificates/目录,且Node.js进程拥有读取权限。
  • 无需为云主机生成证书,但必须使用Azure官方提供的根CA证书(可从Azure门户下载AAD DS根证书),替换当前使用的证书文件。
  • 生产环境建议将rejectUnauthorized设置为true,避免跳过证书验证带来的安全风险,确保ldap_ssl_cert_verif配置项正确。

3. LDAP连接参数验证

  • 确认ldap_server配置的是Azure LDAP服务的正确域名(如yourdomain.aadds.azure.com),而非本地测试地址。
  • 端口逻辑检查:代码中use_tls=true对应389端口(STARTTLS),use_tls=false对应636端口(LDAPS),需与Azure AAD DS的默认配置一致。
  • 绑定DN格式验证:Azure AD的绑定DN格式应为CN=用户名,OU=AADDC Users,DC=yourdomain,DC=aadds,DC=azure,DC=com,确保代码中base_bind_dn的替换逻辑无格式错误。

4. 代码逻辑优化

  • 若启用use_tls,需取消starttls相关代码的注释,确保在绑定前建立加密连接:
    client.starttls({ ca: [ldapCert] }, (err) => {
      if (err) {
        console.error('STARTTLS失败:', err);
        client.unbind();
        resolve(NextResponse.json({ success: false, error: "STARTTLS连接失败!" }, { status: 400 }));
        return;
      }
      // 绑定操作放在starttls成功后执行
      client.bind(...)
    });
    
  • 完善错误处理:在client.on('error')中主动resolve错误响应,避免请求挂起。

内容的提问来源于stack exchange,提问作者Ovais Jetnetix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:56:02