diff --git a/agt-module-system/agt-module-system-server/pom.xml b/agt-module-system/agt-module-system-server/pom.xml
index f374849..aeff863 100644
--- a/agt-module-system/agt-module-system-server/pom.xml
+++ b/agt-module-system/agt-module-system-server/pom.xml
@@ -163,6 +163,11 @@
hutool-extra
+
+ org.springframework.boot
+ spring-boot-starter-data-ldap
+
+
diff --git a/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/controller/admin/user/vo/user/LdapUser.java b/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/controller/admin/user/vo/user/LdapUser.java
new file mode 100644
index 0000000..b0d42a1
--- /dev/null
+++ b/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/controller/admin/user/vo/user/LdapUser.java
@@ -0,0 +1,31 @@
+package org.agt.module.system.controller.admin.user.vo.user;
+
+import lombok.Data;
+import org.springframework.ldap.odm.annotations.Attribute;
+import org.springframework.ldap.odm.annotations.Entry;
+import org.springframework.ldap.odm.annotations.Id;
+
+import javax.naming.Name;
+
+@Data
+@Entry(base = "ou=users", objectClasses = {"inetOrgPerson", "organizationalPerson", "person", "top"})
+public class LdapUser {
+
+ @Id
+ private Name dn;
+
+ @Attribute(name = "cn")
+ private String username;
+
+ @Attribute(name = "sn")
+ private String surname;
+
+ @Attribute(name = "userPassword")
+ private String password;
+
+ @Attribute(name = "mail")
+ private String email;
+
+ @Attribute(name = "gecos")
+ private String gecos;
+}
\ No newline at end of file
diff --git a/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/service/auth/AdminAuthServiceImpl.java b/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/service/auth/AdminAuthServiceImpl.java
index 32e6126..bf97736 100644
--- a/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/service/auth/AdminAuthServiceImpl.java
+++ b/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/service/auth/AdminAuthServiceImpl.java
@@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
import org.agt.framework.common.enums.CommonStatusEnum;
import org.agt.framework.common.enums.UserTypeEnum;
import org.agt.framework.common.util.monitor.TracerUtils;
+import org.agt.framework.common.util.object.BeanUtils;
import org.agt.framework.common.util.servlet.ServletUtils;
import org.agt.framework.common.util.validation.ValidationUtils;
import org.agt.module.system.api.logger.dto.LoginLogCreateReqDTO;
@@ -19,7 +20,17 @@ import org.agt.module.system.api.sms.SmsCodeApi;
import org.agt.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import org.agt.module.system.api.social.dto.SocialUserBindReqDTO;
import org.agt.module.system.api.social.dto.SocialUserRespDTO;
-import org.agt.module.system.controller.admin.auth.vo.*;
+import org.agt.module.system.controller.admin.auth.vo.AuthLoginReqVO;
+import org.agt.module.system.controller.admin.auth.vo.AuthLoginRespVO;
+import org.agt.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
+import org.agt.module.system.controller.admin.auth.vo.AuthResetPasswordByNameReqVO;
+import org.agt.module.system.controller.admin.auth.vo.AuthResetPasswordReqVO;
+import org.agt.module.system.controller.admin.auth.vo.AuthSmsLoginReqVO;
+import org.agt.module.system.controller.admin.auth.vo.AuthSmsSendReqVO;
+import org.agt.module.system.controller.admin.auth.vo.AuthSocialLoginReqVO;
+import org.agt.module.system.controller.admin.auth.vo.CaptchaVerificationReqVO;
+import org.agt.module.system.controller.admin.user.vo.user.LdapUser;
+import org.agt.module.system.controller.admin.user.vo.user.UserSaveReqVO;
import org.agt.module.system.convert.auth.AuthConvert;
import org.agt.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import org.agt.module.system.dal.dataobject.user.AdminUserDO;
@@ -30,17 +41,29 @@ import org.agt.module.system.enums.sms.SmsSceneEnum;
import org.agt.module.system.service.logger.LoginLogService;
import org.agt.module.system.service.member.MemberService;
import org.agt.module.system.service.oauth2.OAuth2TokenService;
+import org.agt.module.system.service.permission.PermissionService;
import org.agt.module.system.service.social.SocialUserService;
import org.agt.module.system.service.user.AdminUserService;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.query.LdapQuery;
+import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Objects;
+import java.util.Set;
import static org.agt.framework.common.exception.util.ServiceExceptionUtil.exception;
import static org.agt.framework.common.util.servlet.ServletUtils.getClientIP;
-import static org.agt.module.system.enums.ErrorCodeConstants.*;
+import static org.agt.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS;
+import static org.agt.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_CAPTCHA_CODE_ERROR;
+import static org.agt.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_USER_DISABLED;
+import static org.agt.module.system.enums.ErrorCodeConstants.AUTH_MOBILE_NOT_EXISTS;
+import static org.agt.module.system.enums.ErrorCodeConstants.AUTH_REGISTER_CAPTCHA_CODE_ERROR;
+import static org.agt.module.system.enums.ErrorCodeConstants.AUTH_THIRD_LOGIN_NOT_BIND;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_MOBILE_NOT_EXISTS;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
/**
* Auth Service 实现类
@@ -67,6 +90,10 @@ public class AdminAuthServiceImpl implements AdminAuthService {
private CaptchaService captchaService;
@Resource
private SmsCodeApi smsCodeApi;
+ @Resource
+ private LdapTemplate ldapTemplate;
+ @Resource
+ private PermissionService permissionService;
/**
* 验证码的开关,默认为 true
@@ -96,13 +123,50 @@ public class AdminAuthServiceImpl implements AdminAuthService {
return user;
}
+ public AdminUserDO authenticateByLdap(String username, String password) {
+
+ final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
+ LdapUser ldapUsers;
+ try {
+ LdapQuery query = LdapQueryBuilder.query()
+ .where("uid").is(username).or("mail").is(username);
+ ldapUsers = ldapTemplate.findOne(query, LdapUser.class);
+ ldapTemplate.authenticate(query, password);
+ } catch (Exception e) {
+ log.error("登录失败:{}", e.getMessage());
+ createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
+ throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
+ }
+ if (ldapUsers == null) {
+ createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
+ throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
+ }
+
+ // 校验账号是否存在
+ AdminUserDO user = userService.getUserByUsername(username);
+ if (user == null) {
+ UserSaveReqVO userSaveReqVO = new UserSaveReqVO();
+ userSaveReqVO.setUsername(username);
+ userSaveReqVO.setPassword(password);
+ userSaveReqVO.setNickname(ldapUsers.getGecos());
+ userSaveReqVO.setEmail(ldapUsers.getEmail());
+ Long userId = userService.createUser(userSaveReqVO);
+ permissionService.assignUserRole(userId, Set.of(3L));
+ user = BeanUtils.toBean(userSaveReqVO, AdminUserDO.class);
+ user.setId(userId);
+ }
+
+ return user;
+ }
+
@Override
public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
// 校验验证码
validateCaptcha(reqVO);
// 使用账号密码,进行登录
- AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
+// AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
+ AdminUserDO user = authenticateByLdap(reqVO.getUsername(), reqVO.getPassword());
// 首次登录不返回token
if (user.getLoginDate() == null) {
diff --git a/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/service/user/AdminUserServiceImpl.java b/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/service/user/AdminUserServiceImpl.java
index 3f53ee1..9739d15 100644
--- a/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/service/user/AdminUserServiceImpl.java
+++ b/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/service/user/AdminUserServiceImpl.java
@@ -4,6 +4,13 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
+import com.google.common.annotations.VisibleForTesting;
+import com.mzt.logapi.context.LogRecordContext;
+import com.mzt.logapi.service.impl.DiffParseFunction;
+import com.mzt.logapi.starter.annotation.LogRecord;
+import jakarta.annotation.Resource;
+import jakarta.validation.ConstraintViolationException;
+import lombok.extern.slf4j.Slf4j;
import org.agt.framework.common.enums.CommonStatusEnum;
import org.agt.framework.common.exception.ServiceException;
import org.agt.framework.common.pojo.PageResult;
@@ -15,6 +22,7 @@ import org.agt.module.infra.api.config.ConfigApi;
import org.agt.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
import org.agt.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
import org.agt.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
+import org.agt.module.system.controller.admin.user.vo.user.LdapUser;
import org.agt.module.system.controller.admin.user.vo.user.UserImportExcelVO;
import org.agt.module.system.controller.admin.user.vo.user.UserImportRespVO;
import org.agt.module.system.controller.admin.user.vo.user.UserPageReqVO;
@@ -31,26 +39,52 @@ import org.agt.module.system.service.dept.DeptService;
import org.agt.module.system.service.dept.PostService;
import org.agt.module.system.service.permission.PermissionService;
import org.agt.module.system.service.tenant.TenantService;
-import com.google.common.annotations.VisibleForTesting;
-import com.mzt.logapi.context.LogRecordContext;
-import com.mzt.logapi.service.impl.DiffParseFunction;
-import com.mzt.logapi.starter.annotation.LogRecord;
-import jakarta.annotation.Resource;
-import jakarta.validation.ConstraintViolationException;
-import lombok.extern.slf4j.Slf4j;
+import org.agt.module.system.util.ldap.SHA512CryptVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.query.LdapQuery;
+import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
import java.time.LocalDateTime;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static org.agt.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static org.agt.framework.common.util.collection.CollectionUtils.*;
-import static org.agt.module.system.enums.ErrorCodeConstants.*;
-import static org.agt.module.system.enums.LogRecordConstants.*;
+import static org.agt.framework.common.util.collection.CollectionUtils.convertList;
+import static org.agt.framework.common.util.collection.CollectionUtils.convertSet;
+import static org.agt.framework.common.util.collection.CollectionUtils.singleton;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_COUNT_MAX;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_EMAIL_EXISTS;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_IMPORT_INIT_PASSWORD;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_IMPORT_LIST_IS_EMPTY;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_IS_DISABLE;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_MOBILE_EXISTS;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_PASSWORD_FAILED;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_REGISTER_DISABLED;
+import static org.agt.module.system.enums.ErrorCodeConstants.USER_USERNAME_EXISTS;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_CREATE_SUB_TYPE;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_CREATE_SUCCESS;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_DELETE_SUB_TYPE;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_DELETE_SUCCESS;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_TYPE;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_UPDATE_PASSWORD_SUCCESS;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_UPDATE_SUB_TYPE;
+import static org.agt.module.system.enums.LogRecordConstants.SYSTEM_USER_UPDATE_SUCCESS;
/**
* 后台用户 Service 实现类
@@ -87,6 +121,8 @@ public class AdminUserServiceImpl implements AdminUserService {
private ConfigApi configApi;
@Autowired
private RoleMapper roleMapper;
+ @Resource
+ private LdapTemplate ldapTemplate;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -199,11 +235,13 @@ public class AdminUserServiceImpl implements AdminUserService {
@Override
public void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO) {
// 校验旧密码密码
- validateOldPassword(id, reqVO.getOldPassword());
+ AdminUserDO user = validateOldPassword(id, reqVO.getOldPassword());
// 执行更新
AdminUserDO updateObj = new AdminUserDO().setId(id);
updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码
userMapper.updateById(updateObj);
+
+ updateLdapPassword(user.getUsername(), reqVO.getNewPassword());
}
@Override
@@ -219,6 +257,8 @@ public class AdminUserServiceImpl implements AdminUserService {
updateObj.setPassword(encodePassword(password)); // 加密密码
userMapper.updateById(updateObj);
+ updateLdapPassword(user.getUsername(), password);
+
// 3. 记录操作日志上下文
LogRecordContext.putVariable("user", user);
LogRecordContext.putVariable("newPassword", updateObj.getPassword());
@@ -235,6 +275,28 @@ public class AdminUserServiceImpl implements AdminUserService {
updateObj.setPassword(encodePassword(password)); // 加密密码
updateObj.setLoginDate(LocalDateTime.now());
userMapper.updateById(updateObj);
+
+ updateLdapPassword(user.getUsername(), password);
+ }
+
+ private void updateLdapPassword(String username, String password) {
+ try {
+ LdapQuery query = LdapQueryBuilder.query()
+ .where("uid").is(username).or("mail").is(username);
+ LdapUser ldapUsers = ldapTemplate.findOne(query, LdapUser.class);
+
+ password = SHA512CryptVerifier.generateHash(password, "OY7N5bGk");
+ // 创建修改操作
+ ModificationItem[] modificationItems = new ModificationItem[1];
+ modificationItems[0] = new ModificationItem(
+ DirContext.REPLACE_ATTRIBUTE,
+ new BasicAttribute("userPassword", password)
+ );
+ ldapTemplate.modifyAttributes(ldapUsers.getDn(), modificationItems);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw exception(USER_PASSWORD_FAILED, e.getMessage());
+ }
}
@Override
@@ -375,7 +437,7 @@ public class AdminUserServiceImpl implements AdminUserService {
}
private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
- Long deptId, Set postIds) {
+ Long deptId, Set postIds) {
// 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
return DataPermissionUtils.executeIgnore(() -> {
// 校验用户存在
@@ -462,11 +524,12 @@ public class AdminUserServiceImpl implements AdminUserService {
/**
* 校验旧密码
+ *
* @param id 用户 id
* @param oldPassword 旧密码
*/
@VisibleForTesting
- void validateOldPassword(Long id, String oldPassword) {
+ AdminUserDO validateOldPassword(Long id, String oldPassword) {
AdminUserDO user = userMapper.selectById(id);
if (user == null) {
throw exception(USER_NOT_EXISTS);
@@ -474,6 +537,17 @@ public class AdminUserServiceImpl implements AdminUserService {
if (!isPasswordMatch(oldPassword, user.getPassword())) {
throw exception(USER_PASSWORD_FAILED);
}
+
+ try {
+ String username = user.getUsername();
+ LdapQuery query = LdapQueryBuilder.query()
+ .where("uid").is(username).or("mail").is(username);
+ ldapTemplate.authenticate(query, oldPassword);
+ } catch (Exception e) {
+ log.error("密码校验失败:{}", e.getMessage());
+ throw exception(USER_PASSWORD_FAILED);
+ }
+ return user;
}
@Override
@@ -496,7 +570,7 @@ public class AdminUserServiceImpl implements AdminUserService {
// 2.1.1 校验字段是否符合要求
try {
ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class).setPassword(initPassword));
- } catch (ConstraintViolationException ex){
+ } catch (ConstraintViolationException ex) {
respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
return;
}
diff --git a/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/util/ldap/SHA512CryptVerifier.java b/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/util/ldap/SHA512CryptVerifier.java
new file mode 100644
index 0000000..61c851f
--- /dev/null
+++ b/agt-module-system/agt-module-system-server/src/main/java/org/agt/module/system/util/ldap/SHA512CryptVerifier.java
@@ -0,0 +1,58 @@
+package org.agt.module.system.util.ldap;
+
+import org.apache.commons.codec.digest.Sha2Crypt;
+
+public class SHA512CryptVerifier {
+
+ /**
+ * 验证密码是否匹配SHA-512 Crypt哈希
+ * @param password 明文密码
+ * @param hash 完整的哈希字符串(包括{CRYPT}前缀)
+ * @return 匹配返回true,否则返回false
+ */
+ public static boolean verifyPassword(String password, String hash) {
+ // 去除{CRYPT}前缀(如果存在)
+ if (hash.startsWith("{CRYPT}")) {
+ hash = hash.substring(7);
+ }
+
+ try {
+ // 使用Apache Commons Codec的Sha2Crypt进行验证
+ String computedHash = Sha2Crypt.sha512Crypt(password.getBytes(), hash);
+ return computedHash.equals(hash);
+ } catch (Exception e) {
+ System.err.println("验证失败: " + e.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * 生成SHA-512 Crypt哈希
+ * @param password 明文密码
+ * @param salt 盐值(可选,如果为null则自动生成)
+ * @return 哈希值(包含{CRYPT}前缀)
+ */
+ public static String generateHash(String password, String salt) {
+ String hash;
+ if (salt != null) {
+ hash = Sha2Crypt.sha512Crypt(password.getBytes(), "$6$" + salt);
+ } else {
+ hash = Sha2Crypt.sha512Crypt(password.getBytes());
+ }
+ return "{CRYPT}" + hash;
+ }
+
+ public static void main(String[] args) {
+ String storedHash = "{CRYPT}$6$OY7N5bGk$WF6pEJOYU1SEySOuvLxVBNo2MRMsNAX4PX9JnzgUTYfDzevl1/fruztCTM0mIuiAUb3eJ//DEYxzFABmOlIzm/";
+ String password = "test"; // 要验证的密码
+
+ // 验证密码
+ boolean isValid = verifyPassword(password, storedHash);
+ System.out.println("密码验证结果: " + isValid);
+
+ // 生成新哈希(使用相同盐值)
+ String newHash = generateHash(password, "OY7N5bGk");
+ System.out.println("生成的哈希: " + newHash);
+ System.out.println("哈希匹配: " + newHash.equals(storedHash));
+ }
+}
\ No newline at end of file
diff --git a/agt-server/src/main/resources/application-local.yaml b/agt-server/src/main/resources/application-local.yaml
index ca78ac6..9d8c812 100644
--- a/agt-server/src/main/resources/application-local.yaml
+++ b/agt-server/src/main/resources/application-local.yaml
@@ -250,4 +250,11 @@ justauth:
--- #################### iot相关配置 TODO 芋艿【IOT】:再瞅瞅 ####################
pf4j:
# pluginsDir: /tmp/
- pluginsDir: ../plugins
\ No newline at end of file
+ pluginsDir: ../plugins
+
+spring:
+ ldap:
+ urls: ldap://192.168.88.205
+ base: dc=agrandtech,dc=com
+ username: uid=root,cn=users,dc=agrandtech,dc=com
+ password: Tian7989!
\ No newline at end of file