在經過Spring Boot的自動化裝配功能及JDK自帶的LDAP模塊,可經過以下幾個簡單步驟實現業務系統自動同步AD域帳號功能。git
1. Java自帶ldap搜索域帳號信息核心代碼:spring
try { LdapContext ctx = new InitialLdapContext(env, null); SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchCtls.setReturningAttributes(new String[]{"name","sn","distinguishedName"}); NamingEnumeration<SearchResult> answer = ctx.search("searchBase", "searchFilter", searchCtls); while (answer.hasMoreElements()) { SearchResult sr = (SearchResult) answer.next(); System.out.println("<<<::[" + sr.getName()+"]::>>>>"); } ctx.close(); } catch (NamingException e) { e.printStackTrace(); }
2. 經過application.properties增長自定義配置app
quickdoc.ldap.username=michael@mxleader.cn quickdoc.ldap.password=chenbichao quickdoc.ldap.url=LDAP://192.168.15.100:389 quickdoc.ldap.searchBase=OU=XXX有限公司,OU=XXX集團,DC=mxleader,DC=cn
3. 增長resources/META-INFO/spring.factories文件,內容以下:ui
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.mxleader.quickdoc.config.LDAPConfiguration
4. 增長屬性Bean文件類LDAPProerties和LDAPConfiguration類, initLdapUsers方法經過Reactor方式實現業務系統啓動時自動同步AD域帳號的邏輯,不會阻塞業務進程。this
@SpringBootConfiguration @ConditionalOnClass(StreamService.class) @EnableConfigurationProperties(LDAPProperties.class) public class LDAPConfiguration { private final LDAPProperties ldapProperties; public LDAPConfiguration(LDAPProperties ldapProperties) { this.ldapProperties = ldapProperties; } @Bean public LDAPService ldapService(ConfigService configService) { Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.SECURITY_AUTHENTICATION, "simple");//"none","simple","strong" env.put(Context.SECURITY_PRINCIPAL, ldapProperties.getUsername()); env.put(Context.SECURITY_CREDENTIALS, ldapProperties.getPassword()); env.put(Context.PROVIDER_URL, ldapProperties.getUrl()); return new LDAPServiceImpl(configService, env, ldapProperties.getSearchBase(), ldapProperties.getBlacklist()); } /** * 初始化LDAP域帳號() * * @param ldapService * @param userService * @return */ @Bean public CommandLineRunner initLdapUsers(LDAPService ldapService, UserService userService, DiskService diskService) { return args -> ldapService.searchLdapUsers() .filter(sysUser -> userService.get(sysUser.getUsername()) == null) .map(userService::saveUser) .subscribe(sysUser -> diskService.save("個人磁盤", new Authorization(sysUser.getUsername(), AuthType.PRIVATE, new HashSet<AuthAction>() {{ add(AuthAction.READ); add(AuthAction.WRITE); add(AuthAction.DELETE); }}))); } }
5. 增長LDAP操做服務接口和實現類url
public interface LDAPService { Flux<SearchResult> searchLdapItems(String searchFilter, String returnedAtts[], String searchBase) throws NamingException; Flux<SysUser> searchLdapUsers(String searchBase) throws NamingException; Flux<SysUser> searchLdapUsers() throws NamingException; Flux<SearchResult> searchLdapGroups(String searchBase) throws NamingException; Flux<SearchResult> searchLdapGroups() throws NamingException; }
public class LDAPServiceImpl implements LDAPService { private final Properties env; private final String defaultSearchBase; private final String blacklist[]; private static final String defaultGroupFilter = "(&(objectCategory=Group)(objectClass=group)(name=*))"; private static final String defaultPersonFilter = "(&(objectCategory=Person)(objectClass=user)(name=*))"; private static final String defaultGroupAtts[] = {"distinguishedName", "name"}; private static final String defaultPersonAtts[] = {"distinguishedName", "memberOf", "name", "sAMAccountName", "displayName", "title", "mail", "department"}; private final ConfigService configService; public LDAPServiceImpl(ConfigService configService, Properties env, String defaultSearchBase,String blacklist[]) { this.configService = configService; this.env = env; this.defaultSearchBase = defaultSearchBase; this.blacklist = blacklist; } public Flux<SearchResult> searchLdapItems(String searchFilter, String returnedAtts[], String searchBase) throws NamingException { LdapContext ctx = new InitialLdapContext(env, null); SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchCtls.setReturningAttributes(returnedAtts); NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls); Flux<SearchResult> searchResultFlux = Flux.fromStream(Collections.list(answer).stream()); ctx.close(); return searchResultFlux; } public Flux<SysUser> searchLdapUsers(String searchBase) throws NamingException { Flux<SysUser> sysUserFlux = searchLdapItems(defaultPersonFilter, defaultPersonAtts, searchBase) .map(sr -> { try { String sn = sr.getAttributes().get("sAMAccountName").get().toString(); String displayName = sr.getAttributes().get("displayName").get().toString(); String title = sr.getAttributes().get("title").get().toString(); String email = sr.getAttributes().get("mail").get().toString(); String department = sr.getAttributes().get("department").get().toString(); return new SysUser(ObjectId.get(), sn, displayName, title, HanyuPinyinUtil.toHanyuPinyin(displayName), configService.getSysProfile().getIconMap().get("AWARD"), true, department, new HashSet<SysUser.Authority>() {{ add(SysUser.Authority.USER); }}, new HashSet<String>() {{ add(department); }}, email); } catch (NamingException exp) { exp.printStackTrace(); return null; } }); // 過濾黑名單部門人員 if(blacklist!=null && blacklist.length>0){ return sysUserFlux.filter(sysUser -> { for (String blackItem : blacklist) { if(sysUser.getDepartment().startsWith(blackItem)) return false; } return true; }); }else { return sysUserFlux; } } public Flux<SysUser> searchLdapUsers() throws NamingException { return searchLdapUsers(defaultSearchBase); } public Flux<SearchResult> searchLdapGroups(String searchBase) throws NamingException { return searchLdapItems(defaultGroupFilter, defaultGroupAtts, searchBase); } public Flux<SearchResult> searchLdapGroups() throws NamingException { return searchLdapGroups(defaultSearchBase); } }
以上邏輯的完整代碼可參考工程:https://gitee.com/mxleader/quick-doc-servicecode
個人博客即將搬運同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=2ztp73q3z1gkk接口