!9 LDAP同步优化

- 优化:[API]LDAP的部门同步逻辑
- 优化:[API]LDAP的用户同步逻辑
This commit is contained in:
白书科技 2024-07-04 08:55:21 +00:00
parent 92a9f85171
commit 6541c3a191
7 changed files with 162 additions and 77 deletions

View File

@ -1,13 +1,18 @@
## v1.7 版本更新
## 1.8 版本更新
- 新增:[API]MinIO配置信息增加环境变量的读取
- 新增:[API]学员学习权限优化
- 新增:[后台]后台首页增加课件数量的显示
- 新增:[后台]线上课列表增加创建人字段
- 优化:[后台]学员部门包含子部门所有学员数量
- 优化:[API]根据分类ID获取所有子分类的课程
- 优化:[API]根据部门ID获取所有父级部门的课程
- 优化:[后台]部门指派器
- 优化:[PC]视频播放器去除右键点击
- 优化:[PC]首页学习时长去掉秒
- 优化:[H5]首页tab切换优化
- 优化:[API]LDAP的部门同步逻辑
- 优化:[API]LDAP的用户同步逻辑
## 1.7 版本更新
- 新增:[API]MinIO配置信息增加环境变量的读取
- 新增:[API]学员学习权限优化
- 新增:[后台]后台首页增加课件数量的显示
- 新增:[后台]线上课列表增加创建人字段
- 优化:[后台]学员部门包含子部门所有学员数量
- 优化:[API]根据分类ID获取所有子分类的课程
- 优化:[API]根据部门ID获取所有父级部门的课程
- 优化:[后台]部门指派器
- 优化:[PC]视频播放器去除右键点击
- 优化:[PC]首页学习时长去掉秒
- 优化:[H5]首页tab切换优化

View File

@ -67,6 +67,9 @@ public class LoginBus {
public HashMap<String, Object> tokenByLdapTransformUser(LdapTransformUser ldapTransformUser)
throws ServiceException {
User user = ldapBus.singleUserSync(ldapTransformUser, appConfigService.defaultAvatar());
if (user == null) {
throw new ServiceException("用户状态异常,无法登录!");
}
return tokenByUser(user);
}
}

View File

@ -119,14 +119,7 @@ public class LoginController {
try {
LdapTransformUser ldapTransformUser =
LdapUtil.loginByMailOrUid(
ldapConfig.getUrl(),
ldapConfig.getAdminUser(),
ldapConfig.getAdminPass(),
ldapConfig.getBaseDN(),
mail,
uid,
req.getPassword());
LdapUtil.loginByMailOrUid(ldapConfig, mail, uid, req.getPassword());
if (ldapTransformUser == null) {
return JsonResponse.error("登录失败.请检查账号和密码");
}

View File

@ -28,7 +28,6 @@ import xyz.playedu.common.exception.NotFoundException;
import xyz.playedu.common.service.*;
import xyz.playedu.common.types.LdapConfig;
import xyz.playedu.common.util.HelperUtil;
import xyz.playedu.common.util.StringUtil;
import xyz.playedu.common.util.ldap.LdapTransformDepartment;
import xyz.playedu.common.util.ldap.LdapTransformUser;
import xyz.playedu.common.util.ldap.LdapUtil;
@ -63,11 +62,7 @@ public class LDAPBus {
LdapConfig ldapConfig = appConfigService.ldapConfig();
List<LdapTransformDepartment> ouList =
LdapUtil.departments(
ldapConfig.getUrl(),
ldapConfig.getAdminUser(),
ldapConfig.getAdminPass(),
ldapConfig.getBaseDN());
LdapUtil.departments(ldapConfig, ldapConfig.getBaseDN());
if (ouList == null || ouList.isEmpty()) {
return;
@ -80,6 +75,7 @@ public class LDAPBus {
// 本地缓存表
HashMap<String, Integer> depIdKeyByName = new HashMap<>();
// 全局排序计数
Integer sort = 0;
@ -90,20 +86,25 @@ public class LDAPBus {
String[] tmpChains = dn.replace("ou=", "").split(",");
String prevName = "";
log.info("#####START#####[dn:{},uuid:{}]", dn, uuid);
// 同步记录
LdapDepartment tmpLdapDepartment = ldapDepartments.get(uuid);
if (tmpLdapDepartment != null && tmpLdapDepartment.getDn().equals(dn)) {
// 当前部门已经同步 && 未发生改变
log.info("LDAP-部门同步处理-未发生改变|dn:{}", dn);
continue;
}
// 执行到这里的有两种情况
// 1.部门未同步
// 2.部门已同步但是发生了变化
// 2.1 部门名称修改
// 2.2 部门上级修改
// |-2.1 部门名称修改
// |-2.2 部门上级名称修改
// |-2.3 层级发生变动(增加层级|减少层级)
int length = tmpChains.length;
for (int i = 0; i < length; i++) {
sort++;
int parentId = 0;
@ -118,34 +119,73 @@ public class LDAPBus {
parentId = depIdKeyByName.get(prevName);
}
// 最后一个记录 && 已存在部门-发生了变动
log.info(
"LDAP-部门同步处理-链处理|ctx=[dn={},fullName:{},tmpName:{},parentId:{},sort:{}]",
dn,
fullName,
tmpName,
parentId,
sort);
if (i + 1 == length && tmpLdapDepartment != null) {
// OU链发生了改变
// 1.部门名改变
// 2.上级部门名改变
// 3.层级改变
log.info("LDAP-部门同步处理-OU链发生改变|ctx=[新:{},旧:{}]", dn, tmpLdapDepartment.getDn());
Department tmpDepartment =
departmentService.findOrFail(tmpLdapDepartment.getDepartmentId());
departmentService.update(tmpDepartment, tmpName, parentId, sort);
if (!tmpDepartment.getName().equals(tmpName)
|| tmpLdapDepartment.getDn().split(",").length
!= dn.split(",").length) {
departmentService.update(tmpDepartment, tmpName, parentId, sort);
}
// 更新同步记录
tmpLdapDepartment.setDn(dn); // 最新的DN
ldapDepartmentService.updateDnById(tmpLdapDepartment.getId(), dn);
// 更新本地缓存
ldapDepartments.put(uuid, tmpLdapDepartment);
// 更新本地缓存
depIdKeyByName.put(fullName, tmpDepartment.getId());
} else {
// 检查本地是否有缓存
Integer depId = depIdKeyByName.get(fullName);
log.info("LDAP-部门同步处理-从缓存查询depId|ctx=[fullName:{},depId:{}]", fullName, depId);
if (depId == null) {
Department tmpDep = departmentService.findByName(tmpName, parentId);
if (tmpDep != null) {
depId = tmpDep.getId();
log.info(
"LDAP-部门同步处理-从数据库查询depId|ctx=[fullName:{},depId:{}]",
fullName,
depId);
} else {
depId = departmentService.create(tmpName, parentId, sort);
// 创建同步记录
ldapDepartmentService.create(depId, uuid, dn);
log.info(
"LDAP-部门同步处理-新建部门|ctx=[fullName:{},depId:{}]", fullName, depId);
}
// 写入本地缓存
depIdKeyByName.put(fullName, depId);
// 写入本地缓存
LdapDepartment storedLdapDepartment = new LdapDepartment();
storedLdapDepartment.setUuid(uuid);
storedLdapDepartment.setDn(dn);
storedLdapDepartment.setDepartmentId(depId);
ldapDepartments.put(uuid, storedLdapDepartment);
}
}
if (i + 1 == length && tmpLdapDepartment == null) {
Integer tmpDepId = depIdKeyByName.get(fullName);
// 创建同步记录
ldapDepartmentService.create(tmpDepId, uuid, dn);
// 写入本地缓存
LdapDepartment storedLdapDepartment = new LdapDepartment();
storedLdapDepartment.setUuid(uuid);
storedLdapDepartment.setDn(dn);
storedLdapDepartment.setDepartmentId(tmpDepId);
ldapDepartments.put(uuid, storedLdapDepartment);
}
// 父级叠加
prevName = fullName;
}
@ -155,23 +195,20 @@ public class LDAPBus {
List<String> uuidList = ouList.stream().map(LdapTransformDepartment::getUuid).toList();
List<LdapDepartment> ldapDepartmentList =
ldapDepartmentService.notChunkByUUIDList(uuidList);
for (LdapDepartment ldapDepartment : ldapDepartmentList) {
// 删除本地部门
departmentService.destroy(ldapDepartment.getDepartmentId());
// 删除关联记录
ldapDepartmentService.destroy(ldapDepartment.getId());
if (ldapDepartmentList != null && !ldapDepartmentList.isEmpty()) {
for (LdapDepartment ldapDepartment : ldapDepartmentList) {
// 删除本地部门
departmentService.destroy(ldapDepartment.getDepartmentId());
// 删除同步记录
ldapDepartmentService.destroy(ldapDepartment.getId());
}
}
}
public void userSync() throws NamingException, IOException {
LdapConfig ldapConfig = appConfigService.ldapConfig();
List<LdapTransformUser> userList =
LdapUtil.users(
ldapConfig.getUrl(),
ldapConfig.getAdminUser(),
ldapConfig.getAdminPass(),
ldapConfig.getBaseDN());
List<LdapTransformUser> userList = LdapUtil.users(ldapConfig, ldapConfig.getBaseDN());
if (userList == null || userList.isEmpty()) {
return;
@ -181,6 +218,10 @@ public class LDAPBus {
for (LdapTransformUser ldapTransformUser : userList) {
if (ldapTransformUser.isBan()) {
log.info(
"LDAP-用户同步-用户被禁止|ctx=[dn:{},uuid={}]",
ldapTransformUser.getDn(),
ldapTransformUser.getId());
continue;
}
singleUserSync(ldapTransformUser, defaultAvatar);
@ -188,6 +229,11 @@ public class LDAPBus {
}
public User singleUserSync(LdapTransformUser ldapTransformUser, String defaultAvatar) {
log.info(
"*****START*****LDAP-用户同步-开始|ctx=[dn:{},uuid:{}]",
ldapTransformUser.getDn(),
ldapTransformUser.getId());
// LDAP用户的名字
String ldapUserName = ldapTransformUser.getCn();
@ -195,23 +241,24 @@ public class LDAPBus {
Integer depId = departmentService.createWithChainList(ldapTransformUser.getOu());
Integer[] depIds = depId == 0 ? null : new Integer[] {depId};
// LDAP用户在本地的缓存记录
LdapUser ldapUser = ldapUserService.findByUUID(ldapTransformUser.getId());
User user;
// LDAP同步记录
LdapUser ldapUser = ldapUserService.findByUUID(ldapTransformUser.getId());
// 计算将LDAP用户关联到本地users表的email字段值
String localUserEmail = ldapTransformUser.getUid();
if (StringUtil.isNotEmpty(ldapTransformUser.getEmail())) {
localUserEmail = ldapTransformUser.getEmail();
}
if (ldapUser == null) {
// 检测localUserEmail是否存在
if (userService.find(localUserEmail) != null) {
localUserEmail = HelperUtil.randomString(5) + "_" + localUserEmail;
log.info("LDAP-用户同步-email重复|ctx=[email:{}]", localUserEmail);
return null;
}
// LDAP用户数据缓存到本地
// 创建同步记录
ldapUser = ldapUserService.store(ldapTransformUser);
// 创建本地user
user =
userService.createWithDepIds(
@ -221,11 +268,26 @@ public class LDAPBus {
HelperUtil.randomString(10),
"",
depIds);
// 将LDAP缓存数据与本地user关联
ldapUserService.updateUserId(ldapUser.getId(), user.getId());
log.info(
"LDAP-用户同步-录入数据|ctx=[userId:{},ldapUserId:{}]", user.getId(), ldapUser.getId());
} else {
log.info(
"LDAP-用户同步-检测变化值|ctx=[新dn:{},旧dn:{}]",
ldapTransformUser.getDn(),
ldapUser.getDn());
user = userService.find(ldapUser.getUserId());
if (user == null) {
// 同步记录创建了但是user却没创建
log.info(
"LDAP-用户同步-同步记录存在但user不存在|ctx=[dn:{},ldapUserId:{}]",
ldapTransformUser.getDn(),
ldapUser.getId());
user =
userService.createWithDepIds(
localUserEmail,
@ -235,6 +297,7 @@ public class LDAPBus {
"",
depIds);
}
// 账号修改[账号有可能是email也有可能是uid]
if (!localUserEmail.equals(user.getEmail())) {
// 检测localUserEmail是否存在
@ -243,19 +306,23 @@ public class LDAPBus {
}
userService.updateEmail(user.getId(), localUserEmail);
}
// ldap-email的变化
if (!ldapUser.getEmail().equals(ldapTransformUser.getEmail())) {
ldapUserService.updateEmail(ldapUser.getId(), ldapTransformUser.getEmail());
}
// ldap-uid的变化
if (!ldapUser.getUid().equals(ldapTransformUser.getUid())) {
ldapUserService.updateUid(ldapUser.getId(), ldapTransformUser.getUid());
}
// 名字同步修改
if (!ldapUserName.equals(ldapUser.getCn())) {
userService.updateName(user.getId(), ldapUserName);
ldapUserService.updateCN(ldapUser.getId(), ldapUserName);
}
// 部门修改同步
String newOU = String.join(",", ldapTransformUser.getOu());
if (!newOU.equals(ldapUser.getOu())) {

View File

@ -30,4 +30,6 @@ public interface LdapDepartmentService extends IService<LdapDepartment> {
void destroy(Integer id);
void create(Integer depId, String uuid, String dn);
void updateDnById(Integer id, String dn);
}

View File

@ -56,4 +56,12 @@ public class LdapDepartmentServiceImpl extends ServiceImpl<LdapDepartmentMapper,
save(ldapDepartment);
}
@Override
public void updateDnById(Integer id, String dn) {
LdapDepartment ldapDepartment = new LdapDepartment();
ldapDepartment.setId(id);
ldapDepartment.setDn(dn);
updateById(ldapDepartment);
}
}

View File

@ -18,6 +18,7 @@ package xyz.playedu.common.util.ldap;
import lombok.extern.slf4j.Slf4j;
import xyz.playedu.common.exception.ServiceException;
import xyz.playedu.common.types.LdapConfig;
import xyz.playedu.common.util.StringUtil;
import java.io.IOException;
@ -83,10 +84,11 @@ public class LdapUtil {
return new InitialLdapContext(context, null);
}
public static List<LdapTransformUser> users(
String url, String adminUser, String adminPass, String baseDN)
public static List<LdapTransformUser> users(LdapConfig ldapConfig, String filterScope)
throws NamingException, IOException {
LdapContext ldapContext = initContext(url, adminUser, adminPass);
LdapContext ldapContext =
initContext(
ldapConfig.getUrl(), ldapConfig.getAdminUser(), ldapConfig.getAdminPass());
int pageSize = 1000;
List<LdapTransformUser> users = new ArrayList<>();
@ -111,11 +113,11 @@ public class LdapUtil {
}
NamingEnumeration<SearchResult> result =
ldapContext.search(baseDN, USER_OBJECT_CLASS, controls);
ldapContext.search(filterScope, USER_OBJECT_CLASS, controls);
while (result.hasMoreElements()) {
SearchResult item = result.nextElement();
if (item != null) {
LdapTransformUser ldapTransformUser = parseTransformUser(item, baseDN);
LdapTransformUser ldapTransformUser = parseTransformUser(item, filterScope);
if (ldapTransformUser != null) {
users.add(ldapTransformUser);
}
@ -153,9 +155,11 @@ public class LdapUtil {
return null;
}
public static List<LdapTransformDepartment> departments(
String url, String adminUser, String adminPass, String baseDN) throws NamingException {
LdapContext ldapContext = initContext(url, adminUser, adminPass);
public static List<LdapTransformDepartment> departments(LdapConfig ldapConfig, String baseDN)
throws NamingException {
LdapContext ldapContext =
initContext(
ldapConfig.getUrl(), ldapConfig.getAdminUser(), ldapConfig.getAdminPass());
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
@ -165,15 +169,16 @@ public class LdapUtil {
String filter = "(objectClass=organizationalUnit)";
NamingEnumeration<SearchResult> result = null;
try {
log.info("LDAP-部门查询|条件[baseDN={},filter={}]", baseDN, filter);
result = ldapContext.search(baseDN, filter, controls);
} catch (NamingException e) {
log.error("LDAP部门查询失败", e);
log.error("LDAP-部门查询-失败|errMsg={}", e.getMessage());
} finally {
closeContext(ldapContext);
}
if (result == null || !result.hasMoreElements()) {
log.info("LDAP部门为空");
log.info("LDAP-部门查询-结果为空|条件[baseDN={},filter={}]", baseDN, filter);
return null;
}
@ -186,6 +191,7 @@ public class LdapUtil {
if (item == null) {
continue;
}
Attributes attributes = item.getAttributes();
if (attributes == null) {
continue;
@ -221,13 +227,7 @@ public class LdapUtil {
}
public static LdapTransformUser loginByMailOrUid(
String url,
String adminUser,
String adminPass,
String baseDN,
String mail,
String uid,
String password)
LdapConfig ldapConfig, String mail, String uid, String password)
throws ServiceException, NamingException {
if (StringUtil.isEmpty(mail) && StringUtil.isEmpty(uid)) {
throw new ServiceException("mail和Uid不能同时为空");
@ -249,10 +249,12 @@ public class LdapUtil {
String filter = String.format("(&%s%s)", userFilter, USER_OBJECT_CLASS);
LdapContext ldapContext = initContext(url, adminUser, adminPass);
LdapContext ldapContext =
initContext(
ldapConfig.getUrl(), ldapConfig.getAdminUser(), ldapConfig.getAdminPass());
NamingEnumeration<SearchResult> result = null;
try {
result = ldapContext.search(baseDN, filter, controls);
result = ldapContext.search(ldapConfig.getBaseDN(), filter, controls);
} catch (NamingException e) {
log.error("LDAP-通过mail或uid登录失败", e);
} finally {
@ -265,7 +267,8 @@ public class LdapUtil {
}
// 根据mail或uid查询出来的用户
LdapTransformUser ldapUser = parseTransformUser(result.nextElement(), baseDN);
LdapTransformUser ldapUser =
parseTransformUser(result.nextElement(), ldapConfig.getBaseDN());
if (ldapUser == null) {
log.info("LDAP-用户不存在");
return null;
@ -275,7 +278,11 @@ public class LdapUtil {
// 登录成功则意味着密码正确
// 登录失败则意味着密码错误
try {
ldapContext = initContext(url, ldapUser.getDn() + "," + baseDN, password);
ldapContext =
initContext(
ldapConfig.getUrl(),
ldapUser.getDn() + "," + ldapConfig.getBaseDN(),
password);
return ldapUser;
} catch (Exception e) {
// 无法登录->密码错误