From 62a32b2be675edc1498c5005c184ab8d0ee873ec Mon Sep 17 00:00:00 2001 From: DouDou <877258667@qq.com> Date: Mon, 8 Sep 2025 11:55:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=9F=E6=8E=A7=E4=BF=AE=E6=94=B9=20=20?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E6=9F=A5=E8=AF=A2=E6=94=B9=E4=B8=BAredis?= =?UTF-8?q?=E4=B8=AD=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spring/common/utils/LdapReadUtils.java | 98 ++++++++++++ .../spring/config/LdapAccountInitLoad.java | 83 ++++++++++ .../service/impl/CheckLdapDirectoryImpl.java | 143 ++++++++++++++++-- .../sys/task/LdapAccountRefreshTask.java | 75 +++++++++ src/main/resources/application-dev.yml | 1 + 5 files changed, 390 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/spring/common/utils/LdapReadUtils.java create mode 100644 src/main/java/com/spring/config/LdapAccountInitLoad.java create mode 100644 src/main/java/com/spring/modules/sys/task/LdapAccountRefreshTask.java diff --git a/src/main/java/com/spring/common/utils/LdapReadUtils.java b/src/main/java/com/spring/common/utils/LdapReadUtils.java new file mode 100644 index 00000000..e681b2f1 --- /dev/null +++ b/src/main/java/com/spring/common/utils/LdapReadUtils.java @@ -0,0 +1,98 @@ +package com.spring.common.utils; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.PartialResultException; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; +import javax.naming.ldap.PagedResultsControl; +import javax.naming.ldap.PagedResultsResponseControl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @ClassName: LdapReadUtils + * @Description: 读取数据 + * @author: LR + * @date: 2025年9月5日 下午12:04:30 + * @Copyright: + */ +public class LdapReadUtils { + private static final Logger logger = LoggerFactory.getLogger(LdapReadUtils.class); + + /** + * + * @Title: getAllLdapAccount + * @Description: 查询所有域控行号 通过分页查询来做 + * @author: LR + * @date 2025年9月5日 下午12:07:09 + * @return: Map + */ + public static Map getAllLdapAccount(LdapContext ctx, String ldapBase) throws NamingException, IOException { + byte[] cookie = null; + // 设置返回所有属性 + SearchControls controls = new SearchControls(); + controls.setReturningAttributes(new String[] { + "sAMAccountName", "displayName", "distinguishedName", "manager" + }); + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); // + + String nameFilter = "(&(objectClass=user)(objectCategory=person)(!(objectClass=computer)))"; + Map ldapAccountMap = new HashMap<>(); + //循环执行 + do{ + ctx.setRequestControls(new Control[] + {new PagedResultsControl(100, cookie, true)}); + NamingEnumeration results = null; + try{ + results = ctx.search(ldapBase, nameFilter, controls); + //获取数据 读取人员组织架构+域控账号 + while (results.hasMore()) { + SearchResult result = results.next(); + Attributes attrs = result.getAttributes(); + String distinguishedName = attrs.get("distinguishedName").get().toString(); + String sAMAccountName = attrs.get("sAMAccountName").get().toString(); + logger.info("distinguishedName:"+distinguishedName); + logger.info("sAMAccountName:"+sAMAccountName); + ldapAccountMap.put(distinguishedName, sAMAccountName); + } + }catch(PartialResultException pre){ + logger.warn("Search results: {}", pre.getMessage()); + }finally{ + ctx.setRequestControls(null); + } + // 处理服务器返回的分页响应 + Control[] controlsResp = ctx.getResponseControls(); + if (controlsResp != null) { + for (Control control : controlsResp) { + if (control instanceof PagedResultsResponseControl) { + PagedResultsResponseControl prrc = (PagedResultsResponseControl) control; + cookie = prrc.getCookie(); + } + } + } else { + cookie = null; + } + + }while(cookie != null && cookie.length > 0); + + + //返回结果数据 + return ldapAccountMap; + } + + + + + + +} diff --git a/src/main/java/com/spring/config/LdapAccountInitLoad.java b/src/main/java/com/spring/config/LdapAccountInitLoad.java new file mode 100644 index 00000000..ffaf14a7 --- /dev/null +++ b/src/main/java/com/spring/config/LdapAccountInitLoad.java @@ -0,0 +1,83 @@ +package com.spring.config; + +import java.util.Map; +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; + +import com.spring.common.utils.LdapReadUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +/** + * + * @ClassName: LdapAccountInitLoad + * @Description: 聚水潭权限数据加载 + * @author: LR + * @date: 2022年11月25日 下午2:50:56 + * @Copyright: + */ +@Component +@Order(value = 12) +public class LdapAccountInitLoad implements ApplicationRunner{ + + private static final Logger logger = LoggerFactory.getLogger(LdapAccountInitLoad.class); + + @Autowired + private RedisTemplate redisTemplate; + + @Value("${spring.ldap.urls}") + private String ldapUrl; + @Value("${spring.ldap.base}") + private String ldapBase; + @Value("${spring.ldap.username}") + private String ldapUserDn; + @Value("${spring.ldap.password}") + private String ldapPassword; + + + + @Override + public void run(ApplicationArguments args) throws Exception { + //首先查询域控账号的所有数据 + Properties env = new Properties(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, ldapUrl); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, ldapUserDn); + env.put(Context.SECURITY_CREDENTIALS, ldapPassword); + env.put(Context.REFERRAL, "ignore"); + env.put("ignorepartialresultexception", "true"); + + LdapContext ctx = null; + + //尝试解析数据 + try{ + // 创建DirContext对象,建立与LDAP服务器的连接 + ctx = new InitialLdapContext(env, null); + //调用方法读取数据 + Map ldapAccountMap = LdapReadUtils.getAllLdapAccount(ctx, ldapBase); + + // 数据放到redis中去 + redisTemplate.opsForHash().putAll("ldapAccount", ldapAccountMap); + logger.info("域控账号缓存的数量:"+ldapAccountMap.size()); + + } catch (NamingException e) { + logger.error("Failed to connect to the LDAP server."); + } finally{ + ctx.close(); + } + + } + +} diff --git a/src/main/java/com/spring/modules/sys/service/impl/CheckLdapDirectoryImpl.java b/src/main/java/com/spring/modules/sys/service/impl/CheckLdapDirectoryImpl.java index 7f6c0411..6cbce744 100644 --- a/src/main/java/com/spring/modules/sys/service/impl/CheckLdapDirectoryImpl.java +++ b/src/main/java/com/spring/modules/sys/service/impl/CheckLdapDirectoryImpl.java @@ -1,7 +1,11 @@ package com.spring.modules.sys.service.impl; import com.spring.modules.sys.service.CheckLdapDirectory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.naming.Context; @@ -25,6 +29,8 @@ import java.util.stream.Collectors; */ @Service public class CheckLdapDirectoryImpl implements CheckLdapDirectory { + + private static final Logger logger = LoggerFactory.getLogger(CheckLdapDirectoryImpl.class); @Value("${spring.ldap.urls}") private String ldapUrl; @@ -34,6 +40,9 @@ public class CheckLdapDirectoryImpl implements CheckLdapDirectory { private String ldapUserDn; @Value("${spring.ldap.password}") private String ldapPassword; + + @Autowired + private RedisTemplate redisTemplate; @Override public boolean checkUserLdapDirectory(String username, String directoryPath) throws NamingException { @@ -132,16 +141,6 @@ public class CheckLdapDirectoryImpl implements CheckLdapDirectory { //返回的数据 Map ldapGroupAccount = new HashMap(); - Properties env = new Properties(); - env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - env.put(Context.PROVIDER_URL, ldapUrl); - env.put(Context.SECURITY_AUTHENTICATION, "simple"); - env.put(Context.SECURITY_PRINCIPAL, ldapUserDn); - env.put(Context.SECURITY_CREDENTIALS, ldapPassword); - - // 创建DirContext对象,建立与LDAP服务器的连接 - DirContext ctx = new InitialDirContext(env); - AclFileAttributeView aclView = Files.getFileAttributeView(securityPath, AclFileAttributeView.class); try { aclView.getAcl().forEach(aclEntry -> { @@ -155,11 +154,23 @@ public class CheckLdapDirectoryImpl implements CheckLdapDirectory { } catch (IOException e) { throw new RuntimeException(e); } + + Properties env = new Properties(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, ldapUrl); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, ldapUserDn); + env.put(Context.SECURITY_CREDENTIALS, ldapPassword); + + // 创建DirContext对象,建立与LDAP服务器的连接 + DirContext ctx = new InitialDirContext(env); + Map ldapAccount = new HashMap(); if (ldapGroupAccount.size() > 0) { //循环检查数据是否存在组信息 如果存在 需要把组数据 替换成账号的信息 for(String strKey : ldapGroupAccount.keySet()) { if (checkMemberIsGroupWithName(ctx, strKey)){ + System.out.println("新Group:"+strKey); ldapAccount.putAll(getLdapAccountListFromGroup(ctx, strKey)); }else { ldapAccount.put(strKey, strKey); @@ -192,6 +203,118 @@ public class CheckLdapDirectoryImpl implements CheckLdapDirectory { NamingEnumeration attrEnum = attrs.getAll(); + while (attrEnum.hasMore()) { + Attribute attr = attrEnum.next(); + // 处理多值属性 + NamingEnumeration values = attr.getAll(); + while (values.hasMore()) { + Object value = values.next(); + String valueStr = value.toString(); + String currentMember = valueStr.substring(valueStr.indexOf("=")+1, valueStr.indexOf(",")); + //从redis中获取数据 + Object ldapAccountObj = redisTemplate.opsForHash().get("ldapAccount", valueStr); + + //如果redis中存在 说明是账号 + if(null != ldapAccountObj){ + //设置Redis中的域控账号 + String ldapAccount = String.valueOf(ldapAccountObj); + accountMap.put(ldapAccount, ldapAccount); + }else if (checkMemberIsGroup(ctx, valueStr)){ + //递归调用 + accountMap.putAll(getLdapAccountListFromGroup(ctx, currentMember)); + }else { + logger.error("异常张哈信息:"+valueStr); + //accountMap.put(getLdapAccountByMemberStr(ctx, valueStr), currentMember); + } + } + } + } + return accountMap; + } + + + /** + * + * @return + * @throws NamingException + * @throws 查询用列表 + */ + public Map getLdapAccountListPage() throws NamingException { + Properties env = new Properties(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, ldapUrl); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, ldapUserDn); + env.put(Context.SECURITY_CREDENTIALS, ldapPassword); + + // 创建DirContext对象,建立与LDAP服务器的连接 + DirContext ctx = new InitialDirContext(env); + Map accountMap = new HashMap<>(); + // 设置返回所有属性 + SearchControls controls = new SearchControls(); + controls.setReturningAttributes(new String[] { + "ou", "cn", "distinguishedName", "description", + "name", "displayName", "objectClass" + }); + controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); // 只查询一级子OU + String nameFilter = "(objectClass=organizationalUnit)"; + // 执行查询 + NamingEnumeration results = ctx.search(ldapBase, nameFilter, controls); + while (results.hasMore()) { + SearchResult result = results.next(); + Attributes attrs = result.getAttributes(); + + NamingEnumeration attrEnum = attrs.getAll(); + + while (attrEnum.hasMore()) { + Attribute attr = attrEnum.next(); + // 处理多值属性 + NamingEnumeration values = attr.getAll(); + while (values.hasMore()) { + Object value = values.next(); + String valueStr = value.toString(); + String currentMember = valueStr.substring(valueStr.indexOf("=")+1, valueStr.indexOf(",")); + if (checkMemberIsGroup(ctx, valueStr)){ + //递归调用 + accountMap.putAll(getLdapAccountListFromGroup(ctx, currentMember)); + }else { + accountMap.put(getLdapAccountByMemberStr(ctx, valueStr), currentMember); + } + } + } + } + return accountMap; + } + + + /** + * + * @param ctx + * @param groupDn + * @desc 新调试方法 + * @throws NamingException + */ + public Map getLdapAccountListFromGroupNew(DirContext ctx, String groupDn) throws NamingException { + Map accountMap = new HashMap<>(); + + // 第一步:直接搜索组内的所有用户,一次性获取域账号 + SearchControls userControls = new SearchControls(); + userControls.setReturningAttributes(new String[] {"sAMAccountName", "cn", "distinguishedName"}); + userControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + // 查询直接属于该组的用户 + String userFilter = "(&(objectClass=user)(memberOf=CN=" + groupDn + "," + ldapBase + "))"; + // 执行查询 + NamingEnumeration userResults = ctx.search(ldapBase, userFilter, userControls); + while (userResults.hasMore()) { + SearchResult result = userResults.next(); + Attributes attrs = result.getAttributes(); + + Attribute samAttr = attrs.get("sAMAccountName"); + Attribute cnAttr = attrs.get("cn"); + System.out.println(samAttr+","+cnAttr); + NamingEnumeration attrEnum = attrs.getAll(); + while (attrEnum.hasMore()) { Attribute attr = attrEnum.next(); // 处理多值属性 diff --git a/src/main/java/com/spring/modules/sys/task/LdapAccountRefreshTask.java b/src/main/java/com/spring/modules/sys/task/LdapAccountRefreshTask.java new file mode 100644 index 00000000..e86b8635 --- /dev/null +++ b/src/main/java/com/spring/modules/sys/task/LdapAccountRefreshTask.java @@ -0,0 +1,75 @@ +package com.spring.modules.sys.task; + +import com.spring.common.utils.LdapReadUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; + +/** + * 定时同步域控账号信息到 Redis + */ +@Component +@EnableScheduling +public class LdapAccountRefreshTask { + private static final Logger logger = LoggerFactory.getLogger(LdapAccountRefreshTask.class); + @Value("${spring.ldap.urls}") + private String ldapUrl; + @Value("${spring.ldap.base}") + private String ldapBase; + @Value("${spring.ldap.username}") + private String ldapUserDn; + @Value("${spring.ldap.password}") + private String ldapPassword; + + @Autowired + private RedisTemplate redisTemplate; + + @Scheduled(cron = "${task.data.refreshLdapAccountToRedis}") + public void refreshLdapAccountToRedis() throws NamingException { + //首先查询域控账号的所有数据 + Properties env = new Properties(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, ldapUrl); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, ldapUserDn); + env.put(Context.SECURITY_CREDENTIALS, ldapPassword); + env.put(Context.REFERRAL, "ignore"); + + LdapContext ctx = null; + + //尝试解析数据 + try{ + // 创建DirContext对象,建立与LDAP服务器的连接 + ctx = new InitialLdapContext(env, null); + //调用方法读取数据 + Map ldapAccountMap = LdapReadUtils.getAllLdapAccount(ctx, ldapBase); + + // 数据放到redis中去 + redisTemplate.opsForHash().putAll("ldapAccount", ldapAccountMap); + + } catch (NamingException | IOException e) { + logger.error("Failed to connect to the LDAP server."); + } finally{ + if (null != ctx){ + ctx.close(); + } + } + } + + +} + + diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 52da19b6..8e888ea7 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -102,6 +102,7 @@ task: syncLocationToPLM: 0 0 0 29 2 ? # 每4年执行 sync_part_catalog_to_plm: 0 0 0 29 2 ? # 每1分钟执行 syncInventoryPartToPlm: 0 0 0 29 2 ? # 每4年执行 + refreshLdapAccountToRedis: 0 0 0 * * ? # flag: false # 单点登录