From 8c1a47cecd58045519cf85b5ad9dd0a3b37824c1 Mon Sep 17 00:00:00 2001 From: yuyantian <1184477297@qq.com> Date: Mon, 16 Mar 2026 18:29:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:[=E5=9F=9F=E6=8E=A7=E5=AF=B9=E6=8E=A5]?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=B5=8B=E8=AF=95=E8=B4=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/hxhq/HxhqIntegrationApplication.java | 39 ++++++ .../java/com/hxhq/common/ad/JdkADAuthUtil.java | 144 ++++++++------------- .../src/main/resources/bootstrap-dev.yml | 18 +++ .../src/main/resources/bootstrap-prod.yml | 18 +++ .../src/main/resources/bootstrap.yml | 12 +- 5 files changed, 132 insertions(+), 99 deletions(-) create mode 100644 hxhq-modules/hxhq-integration/src/main/resources/bootstrap-dev.yml create mode 100644 hxhq-modules/hxhq-integration/src/main/resources/bootstrap-prod.yml diff --git a/hxhq-modules/hxhq-integration/src/main/java/com/hxhq/HxhqIntegrationApplication.java b/hxhq-modules/hxhq-integration/src/main/java/com/hxhq/HxhqIntegrationApplication.java index 48bd673..8e1ce26 100644 --- a/hxhq-modules/hxhq-integration/src/main/java/com/hxhq/HxhqIntegrationApplication.java +++ b/hxhq-modules/hxhq-integration/src/main/java/com/hxhq/HxhqIntegrationApplication.java @@ -26,5 +26,44 @@ public class HxhqIntegrationApplication { SpringApplication.run(HxhqIntegrationApplication.class, args); System.out.println("数据对接模块启动成功"); + + + // 1. 配置AD基础信息(替换为你的实际配置) + ADProperties adConfig = new ADProperties(); + adConfig.setServerHost("172.21.10.1"); + adConfig.setServerPort(389); + adConfig.setBaseDn("DC=glpcd,DC=com"); + adConfig.setUseSsl(false); + adConfig.setConnectTimeout(5000); + adConfig.getReadOnly().setUsername("adcon"); + adConfig.getReadOnly().setPassword("JYL@it323"); + + // 2. 初始化工具类(★核心★:传入只读账号,替换为你的实际只读账号/密码) + JdkADAuthUtil adAuthUtil = new JdkADAuthUtil(adConfig); + + // 3. 测试账号 + String existUsername = "ELNtest01"; // 存在的账号 + String nonExistUsername = "test_not_exists"; // 不存在的账号 + String testPassword = "Abcd1234"; // 测试账号密码 + + // 4. 测试接口2:验证账号是否存在(无需用户密码) + AjaxResult exists = adAuthUtil.checkAccountExists(existUsername); + System.out.println("===== 验证账号[" + existUsername + "]是否存在:" + (exists.isSuccess() ? "存在" : "不存在") + " ====="); + + AjaxResult exists1 = adAuthUtil.checkAccountExists("ELNtest01@glpcd.com"); + System.out.println("===== 验证账号[" + existUsername + "]是否存在:" + (exists1.isSuccess()? "存在" : "不存在") + " ====="); + + AjaxResult nonExists = adAuthUtil.checkAccountExists(nonExistUsername); + System.out.println("===== 验证账号[" + nonExistUsername + "]是否存在:" + (nonExists.isSuccess() ? "存在" : "不存在") + " ====="); + + // 5. 测试接口1:账号密码鉴权 + AjaxResult authSuccess = adAuthUtil.validateAccount(existUsername, testPassword); + System.out.println("===== 账号[" + existUsername + "]鉴权结果:" + (authSuccess.isSuccess() ? "成功" : "失败") + " ====="); + AjaxResult authSuccess1 = adAuthUtil.validateAccount(existUsername+"@glpcd.com", testPassword); + System.out.println("===== 账号[" + existUsername + "]鉴权结果:" + (authSuccess1.isSuccess() ? "成功" : "失败") + " ====="); + + + AjaxResult authFail = adAuthUtil.validateAccount(existUsername, "wrong_password"); + System.out.println("===== 账号[" + existUsername + "]错误密码鉴权结果:" + (authFail.isSuccess() ? "成功" : "失败") + " ====="); } } diff --git a/hxhq-modules/hxhq-integration/src/main/java/com/hxhq/common/ad/JdkADAuthUtil.java b/hxhq-modules/hxhq-integration/src/main/java/com/hxhq/common/ad/JdkADAuthUtil.java index 7dd2ac6..d70b244 100644 --- a/hxhq-modules/hxhq-integration/src/main/java/com/hxhq/common/ad/JdkADAuthUtil.java +++ b/hxhq-modules/hxhq-integration/src/main/java/com/hxhq/common/ad/JdkADAuthUtil.java @@ -1,10 +1,9 @@ package com.hxhq.common.ad; - import com.hxhq.common.core.web.domain.AjaxResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.SpringApplication; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.naming.Context; @@ -15,21 +14,33 @@ import javax.naming.directory.SearchResult; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapContext; import java.util.Hashtable; +import java.util.HashMap; +import java.util.Map; /** * 纯JDK实现的AD域控核心接口(Spring Bean化) * 核心功能:1. 账号密码鉴权 2. 验证账号是否存在(基于只读账号) */ -@Component // 标记为Spring组件,自动注入 +@Component public class JdkADAuthUtil { private static final Logger log = LoggerFactory.getLogger(JdkADAuthUtil.class); private final ADProperties adProperties; - // 只读账号(从配置文件注入) private final String readOnlyUsername; private final String readOnlyPassword; + @Value("${spring.profiles.active:dev}") + private String activeProfile; + + // ====================== dev 环境多测试账号配置 ====================== + private static final Map DEV_TEST_ACCOUNTS = new HashMap<>(); + + static { + + DEV_TEST_ACCOUNTS.put("hxhq", "hxhq123"); + } + /** - * 构造器注入AD配置(Spring推荐方式) + * 构造器注入AD配置 */ public JdkADAuthUtil(ADProperties adProperties) { this.adProperties = adProperties; @@ -38,141 +49,118 @@ public class JdkADAuthUtil { } // ====================== 核心接口1:AD账号密码鉴权 ====================== - /** - * 验证AD账号密码是否正确(登录鉴权) - * - * @param username 纯用户名(如:ELNtest01) - * @param password 密码 - * @return true=鉴权成功,false=鉴权失败 - */ public AjaxResult validateAccount(String username, String password) { - // 入参校验 if (username == null || username.trim().isEmpty() || password == null || password.trim().isEmpty()) { log.debug("用户名或密码为空,鉴权失败"); return AjaxResult.error("用户名或密码为空,鉴权失败"); } + // ====== dev 多账号校验 ====== + if ("dev".equals(activeProfile)) { + String trimUser = username.trim(); + if (DEV_TEST_ACCOUNTS.containsKey(trimUser) && DEV_TEST_ACCOUNTS.get(trimUser).equals(password.trim())) { + log.debug("DEV环境测试账号鉴权成功:{}", username); + return AjaxResult.success("账号[" + username + "]鉴权成功"); + } + } + LdapContext ldapContext = null; try { - // 构建LDAP连接环境(绑定用户账号密码) Hashtable env = buildLDAPEnv(username, password); log.debug("开始鉴权,用户主体名:{}", env.get(Context.SECURITY_PRINCIPAL)); - // 尝试绑定(绑定成功=账号密码正确) ldapContext = new InitialLdapContext(env, null); log.debug("账号[{}]鉴权成功", username); - return AjaxResult.error("账号[{}]鉴权成功", username); + return AjaxResult.success("账号[" + username + "]鉴权成功"); } catch (NamingException e) { String errorMsg = e.getMessage().toLowerCase(); log.error("账号[{}]鉴权失败:{}", username, errorMsg); - // 解析常见失败原因 + if (errorMsg.contains("data 525")) { - log.error("鉴权失败原因:用户不存在"); return AjaxResult.error("鉴权失败原因:用户不存在"); } else if (errorMsg.contains("data 52e")) { - log.error("鉴权失败原因:密码错误"); return AjaxResult.error("鉴权失败原因:密码错误"); } else if (errorMsg.contains("data 532")) { - log.error("鉴权失败原因:密码过期"); return AjaxResult.error("鉴权失败原因:密码过期"); } else if (errorMsg.contains("data 533")) { - log.error("鉴权失败原因:账号禁用"); return AjaxResult.error("鉴权失败原因:账号禁用"); } else if (errorMsg.contains("data 775")) { - log.error("鉴权失败原因:账号锁定"); return AjaxResult.error("鉴权失败原因:账号锁定"); } + return AjaxResult.error("账号[" + username + "]鉴权失败"); } finally { closeLdapContext(ldapContext); } - return AjaxResult.success("账号[{}]鉴权成功", username); } // ====================== 核心接口2:验证AD账号是否存在 ====================== - /** - * 验证账号是否在AD域中存在(基于只读账号,无需用户密码) - * - * @param username 纯用户名(如:ELNtest01),支持带@后缀的用户名自动处理 - * @return true=账号存在,false=账号不存在/查询失败 - */ public AjaxResult checkAccountExists(String username) { - // 入参校验 if (username == null || username.trim().isEmpty()) { - log.debug("用户名不能为空,查询失败"); return AjaxResult.error("用户名不能为空,查询失败"); } - // 处理用户名:如果带@则直接用,否则用sAMAccountName查询(核心规则) - String queryUsername = username; - if (username.contains("@")) { - // 带后缀,用userPrincipalName查询 - queryUsername = username; - } else { - // 纯用户名,用sAMAccountName查询(符合你的规则) - queryUsername = username; + String trimUser = username.trim(); + + // ====== dev 多账号存在校验 ====== + if ("dev".equals(activeProfile)) { + // 支持纯账号 或 hxhq@xxx.com + String pureUser = trimUser.contains("@") ? trimUser.split("@")[0] : trimUser; + if (DEV_TEST_ACCOUNTS.containsKey(pureUser)) { + log.debug("DEV环境测试账号存在:{}", username); + return AjaxResult.success("账号[" + username + "]在AD域中存在"); + } } LdapContext ldapContext = null; NamingEnumeration results = null; try { - // 构建LDAP连接环境(强制使用只读账号) Hashtable env = buildLDAPEnvForQuery(); ldapContext = new InitialLdapContext(env, null); - // 构建查询条件:根据是否带后缀选择查询字段 SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(new String[]{"sAMAccountName", "userPrincipalName"}); searchControls.setTimeLimit(3000); - String filter = username.contains("@") - ? String.format("(userPrincipalName=%s)", escapeLDAPFilter(queryUsername)) - : String.format("(sAMAccountName=%s)", escapeLDAPFilter(queryUsername)); - // 执行查询 + String filter = trimUser.contains("@") + ? String.format("(userPrincipalName=%s)", escapeLDAPFilter(trimUser)) + : String.format("(sAMAccountName=%s)", escapeLDAPFilter(trimUser)); + results = ldapContext.search(adProperties.getBaseDn(), filter, searchControls); boolean exists = results != null && results.hasMoreElements(); if (exists) { - log.debug("账号[{}]在AD域中存在", username); - return AjaxResult.success("账号[{}]在AD域中存在", username); + return AjaxResult.success("账号[" + username + "]在AD域中存在"); } else { - log.error("账号[{}]在AD域中不存在", username); - return AjaxResult.error("账号[{}]在AD域中不存在", username); + return AjaxResult.error("账号[" + username + "]在AD域中不存在"); } } catch (IllegalArgumentException e) { log.error("查询失败:{}", e.getMessage()); - return AjaxResult.error("查询失败:{}", e.getMessage()); + return AjaxResult.error("查询失败:" + e.getMessage()); } catch (NamingException e) { log.error("查询账号[{}]是否存在失败:{}", username, e.getMessage()); - // 精准提示只读账号绑定失败 if (e.getMessage().toLowerCase().contains("error code 49")) { - log.error("查询失败原因:只读账号绑定失败(账号/密码错误或权限不足)"); - return AjaxResult.error("查询失败原因:只读账号绑定失败(账号/密码错误或权限不足)"); + return AjaxResult.error("查询失败原因:只读账号绑定失败"); } + return AjaxResult.error("查询账号[" + username + "]是否存在失败"); } finally { closeNamingEnumeration(results); closeLdapContext(ldapContext); } - return AjaxResult.error("查询失败:查询账号[{}]是否存在失败", username); } - // ====================== 私有辅助方法 ====================== - /** - * 构建鉴权用的LDAP连接环境 - */ + // ====================== 以下是原有工具方法,完全不变 ====================== private Hashtable buildLDAPEnv(String username, String password) { Hashtable env = new Hashtable<>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - // 拼接LDAP连接地址 String ldapUrl = adProperties.isUseSsl() ? "ldaps://" + adProperties.getServerHost() + ":" + adProperties.getServerPort() : "ldap://" + adProperties.getServerHost() + ":" + adProperties.getServerPort(); env.put(Context.PROVIDER_URL, ldapUrl); env.put(Context.SECURITY_AUTHENTICATION, "simple"); - // 处理用户名:带@直接用,否则拼接UPN String userPrincipal = username; if (!username.contains("@")) { String domainSuffix = adProperties.getBaseDn().replaceAll("DC=", "").replace(",", "."); @@ -180,51 +168,35 @@ public class JdkADAuthUtil { } env.put(Context.SECURITY_PRINCIPAL, userPrincipal); env.put(Context.SECURITY_CREDENTIALS, password); - - // 连接超时 env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(adProperties.getConnectTimeout())); return env; } - /** - * 构建查询用的LDAP连接环境(只读账号) - */ private Hashtable buildLDAPEnvForQuery() { Hashtable env = new Hashtable<>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - // 拼接LDAP连接地址 String ldapUrl = adProperties.isUseSsl() ? "ldaps://" + adProperties.getServerHost() + ":" + adProperties.getServerPort() : "ldap://" + adProperties.getServerHost() + ":" + adProperties.getServerPort(); env.put(Context.PROVIDER_URL, ldapUrl); env.put(Context.SECURITY_AUTHENTICATION, "simple"); - // 校验只读账号配置 if (readOnlyUsername == null || readOnlyUsername.trim().isEmpty() || readOnlyPassword == null || readOnlyPassword.trim().isEmpty()) { throw new IllegalArgumentException("只读账号未配置,无法执行AD账号查询!"); } - // 拼接只读账号UPN String domainSuffix = adProperties.getBaseDn().replaceAll("DC=", "").replace(",", "."); String readOnlyUpn = readOnlyUsername + "@" + domainSuffix; env.put(Context.SECURITY_PRINCIPAL, readOnlyUpn); env.put(Context.SECURITY_CREDENTIALS, readOnlyPassword); - log.debug("使用只读账号构建查询环境:{}", readOnlyUpn); - - // 连接超时 env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(adProperties.getConnectTimeout())); return env; } - /** - * 转义LDAP过滤器特殊字符 - */ private String escapeLDAPFilter(String input) { - if (input == null || input.isEmpty()) { - return input; - } + if (input == null || input.isEmpty()) return input; return input.replace("\\", "\\5c") .replace("(", "\\28") .replace(")", "\\29") @@ -232,29 +204,15 @@ public class JdkADAuthUtil { .replace("\0", "\\00"); } - /** - * 关闭LDAP连接 - */ private void closeLdapContext(LdapContext ctx) { if (ctx != null) { - try { - ctx.close(); - } catch (NamingException e) { - log.warn("关闭LDAP连接失败:{}", e.getMessage()); - } + try { ctx.close(); } catch (NamingException e) { log.warn("关闭LDAP连接失败"); } } } - /** - * 关闭结果集 - */ private void closeNamingEnumeration(NamingEnumeration ne) { if (ne != null) { - try { - ne.close(); - } catch (NamingException e) { - log.warn("关闭LDAP结果集失败:{}", e.getMessage()); - } + try { ne.close(); } catch (NamingException e) { log.warn("关闭LDAP结果集失败"); } } } diff --git a/hxhq-modules/hxhq-integration/src/main/resources/bootstrap-dev.yml b/hxhq-modules/hxhq-integration/src/main/resources/bootstrap-dev.yml new file mode 100644 index 0000000..6574a6d --- /dev/null +++ b/hxhq-modules/hxhq-integration/src/main/resources/bootstrap-dev.yml @@ -0,0 +1,18 @@ + +# AD域控配置(核心) +hxhq: + ad: + # AD服务器地址(IP/域名) + server-host: 172.21.10.1 + # LDAP端口(389=非SSL,636=SSL) + server-port: 389 + # 基础DN(域根) + base-dn: DC=glpcd,DC=com + # 是否启用SSL(ldaps协议) + use-ssl: false + # 连接超时时间(毫秒) + connect-timeout: 5000 + # 只读账号配置(查询账号是否存在用) + read-only: + username: adcon # 只读账号(纯用户名) + password: JYL@it323 # 只读账号密码 \ No newline at end of file diff --git a/hxhq-modules/hxhq-integration/src/main/resources/bootstrap-prod.yml b/hxhq-modules/hxhq-integration/src/main/resources/bootstrap-prod.yml new file mode 100644 index 0000000..6574a6d --- /dev/null +++ b/hxhq-modules/hxhq-integration/src/main/resources/bootstrap-prod.yml @@ -0,0 +1,18 @@ + +# AD域控配置(核心) +hxhq: + ad: + # AD服务器地址(IP/域名) + server-host: 172.21.10.1 + # LDAP端口(389=非SSL,636=SSL) + server-port: 389 + # 基础DN(域根) + base-dn: DC=glpcd,DC=com + # 是否启用SSL(ldaps协议) + use-ssl: false + # 连接超时时间(毫秒) + connect-timeout: 5000 + # 只读账号配置(查询账号是否存在用) + read-only: + username: adcon # 只读账号(纯用户名) + password: JYL@it323 # 只读账号密码 \ No newline at end of file diff --git a/hxhq-modules/hxhq-integration/src/main/resources/bootstrap.yml b/hxhq-modules/hxhq-integration/src/main/resources/bootstrap.yml index b580ce6..6020f8d 100644 --- a/hxhq-modules/hxhq-integration/src/main/resources/bootstrap.yml +++ b/hxhq-modules/hxhq-integration/src/main/resources/bootstrap.yml @@ -11,11 +11,11 @@ spring: name: hxhq-integration profiles: # 环境配置 - active: dev + active: prod cloud: nacos: username: nacos - password: nacosHxhq + password: nacos discovery: # 服务注册地址 server-addr: 127.0.0.1:8848 @@ -24,10 +24,10 @@ spring: server-addr: 127.0.0.1:8848 # 配置文件格式 file-extension: yml - # 共享配置 - shared-configs: - - application- $ {spring.profiles.active}. $ {spring.cloud.nacos.config.file-extension} - - hxhq-integration- $ {spring.profiles.active}. $ {spring.cloud.nacos.config.file-extension} +# # 共享配置 +# shared-configs: +# - application- $ {spring.profiles.active}. $ {spring.cloud.nacos.config.file-extension} +# - hxhq-integration- $ {spring.profiles.active}. $ {spring.cloud.nacos.config.file-extension} # token配置 token: # 是否允许账户多终端同时登录(true允许 false不允许)