| @@ -1,6 +1,6 @@ | |||
| # spring-boot-demo-ldap | |||
| > 此 demo 主要演示了 Spring Boot 如何集成 `spring-boot-starter-data-ldap` 完成对 Ldap 的基本CURD操作, 并给出以登录为实战的api 示例 | |||
| > 此 demo 主要演示了 Spring Boot 如何集成 `spring-boot-starter-data-ldap` 完成对 Ldap 的基本 CURD操作, 并给出以登录为实战的 API 示例 | |||
| ## docker openldap 安装步骤 | |||
| @@ -18,7 +18,7 @@ | |||
| ## pom.xml | |||
| ``` | |||
| ```xml | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| @@ -68,25 +68,24 @@ | |||
| </dependencies> | |||
| </project> | |||
| ``` | |||
| ## application.yml | |||
| ```yaml | |||
| spring: | |||
| ldap: | |||
| urls: ldap://localhost:389 | |||
| base: dc=example,dc=org | |||
| username: cn=admin,dc=example,dc=org | |||
| password: admin | |||
| ``` | |||
| ## Person.java | |||
| > 实体类 | |||
| > @Entry 注解 映射ldap对象关系 | |||
| ``` | |||
| package com.xkcoding.ldap.entity; | |||
| import lombok.Data; | |||
| import org.springframework.ldap.odm.annotations.Attribute; | |||
| import org.springframework.ldap.odm.annotations.DnAttribute; | |||
| import org.springframework.ldap.odm.annotations.Entry; | |||
| import org.springframework.ldap.odm.annotations.Id; | |||
| import javax.naming.Name; | |||
| import java.io.Serializable; | |||
| ```java | |||
| /** | |||
| * People | |||
| * | |||
| @@ -158,20 +157,11 @@ public class Person implements Serializable { | |||
| */ | |||
| private String loginShell; | |||
| } | |||
| ``` | |||
| ## PersonRepository.java | |||
| > person 数据持久层 | |||
| ``` | |||
| package com.xkcoding.ldap.repository; | |||
| import com.xkcoding.ldap.entity.Person; | |||
| import org.springframework.data.repository.CrudRepository; | |||
| import org.springframework.stereotype.Repository; | |||
| import javax.naming.Name; | |||
| ```java | |||
| /** | |||
| * PersonRepository | |||
| * | |||
| @@ -189,42 +179,12 @@ public interface PersonRepository extends CrudRepository<Person, Name> { | |||
| * @return com.xkcoding.ldap.entity.Person | |||
| */ | |||
| Person findByUid(String uid); | |||
| /** | |||
| * 查询全部 | |||
| * @return | |||
| */ | |||
| @Override | |||
| Iterable<Person> findAll(); | |||
| /** | |||
| * 保存 | |||
| * @param s | |||
| * @param <S> | |||
| * @return | |||
| */ | |||
| @Override | |||
| <S extends Person> S save(S s); | |||
| /** | |||
| * 删除 | |||
| * @param person | |||
| */ | |||
| @Override | |||
| void delete(Person person); | |||
| } | |||
| ``` | |||
| ## PersonService.java | |||
| > 数据操作服务 | |||
| ``` | |||
| package com.xkcoding.ldap.service; | |||
| import com.xkcoding.ldap.entity.Person; | |||
| import com.xkcoding.ldap.entity.Result; | |||
| import com.xkcoding.ldap.request.LoginRequest; | |||
| ```java | |||
| /** | |||
| * PersonService | |||
| * | |||
| @@ -236,54 +196,40 @@ public interface PersonService { | |||
| /** | |||
| * 登录 | |||
| * @param request com.xkcoding.ldap.request.LoginRequest | |||
| * @return com.xkcoding.ldap.entity.Result | |||
| * | |||
| * @param request {@link LoginRequest} | |||
| * @return {@link Result} | |||
| */ | |||
| Result login(LoginRequest request); | |||
| /** | |||
| * 查询全部 | |||
| * @return com.xkcoding.ldap.entity.Result | |||
| * | |||
| * @return {@link Result} | |||
| */ | |||
| Result listAllPerson(); | |||
| /** | |||
| * 保存 | |||
| * @param person com.xkcoding.ldap.entity.Person | |||
| * | |||
| * @param person {@link Person} | |||
| */ | |||
| void save(Person person); | |||
| /** | |||
| * 删除 | |||
| * @param person com.xkcoding.ldap.entity.Person | |||
| * | |||
| * @param person {@link Person} | |||
| */ | |||
| void delete(Person person); | |||
| } | |||
| } | |||
| ``` | |||
| ## PersonServiceImpl.java | |||
| > person数据操作服务具体逻辑实现类 | |||
| ``` | |||
| package com.xkcoding.ldap.service.impl; | |||
| import com.xkcoding.ldap.entity.Person; | |||
| import com.xkcoding.ldap.entity.Result; | |||
| import com.xkcoding.ldap.exception.ServiceException; | |||
| import com.xkcoding.ldap.repository.PersonRepository; | |||
| import com.xkcoding.ldap.request.LoginRequest; | |||
| import com.xkcoding.ldap.service.PersonService; | |||
| import com.xkcoding.ldap.util.LdapUtils; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.springframework.stereotype.Service; | |||
| import org.springframework.util.ObjectUtils; | |||
| import org.springframework.util.StringUtils; | |||
| import javax.annotation.Resource; | |||
| import java.security.NoSuchAlgorithmException; | |||
| import java.util.List; | |||
| ```java | |||
| /** | |||
| * PersonServiceImpl | |||
| * | |||
| @@ -293,20 +239,24 @@ import java.util.List; | |||
| */ | |||
| @Slf4j | |||
| @Service | |||
| @RequiredArgsConstructor(onConstructor_ = @Autowired) | |||
| public class PersonServiceImpl implements PersonService { | |||
| private final PersonRepository personRepository; | |||
| @Resource | |||
| private PersonRepository personRepository; | |||
| /** | |||
| * 登录 | |||
| * | |||
| * @param request {@link LoginRequest} | |||
| * @return {@link Result} | |||
| */ | |||
| @Override | |||
| public Result login(LoginRequest request) { | |||
| log.info("IN LDAP auth"); | |||
| Person user = personRepository.findByUid(request.getUsername()); | |||
| try { | |||
| if(ObjectUtils.isEmpty(user)) { | |||
| if (ObjectUtils.isEmpty(user)) { | |||
| throw new ServiceException("用户名或密码错误,请重新尝试"); | |||
| } else { | |||
| user.setUserPassword(LdapUtils.asciiToString(user.getUserPassword())); | |||
| @@ -322,46 +272,46 @@ public class PersonServiceImpl implements PersonService { | |||
| return Result.success(user); | |||
| } | |||
| /** | |||
| * 查询全部 | |||
| * | |||
| * @return {@link Result} | |||
| */ | |||
| @Override | |||
| public Result listAllPerson() { | |||
| Iterable<Person> personList = personRepository.findAll(); | |||
| personList.forEach(person -> { | |||
| person.setUserPassword(LdapUtils.asciiToString(person.getUserPassword())); | |||
| }); | |||
| personList.forEach(person -> person.setUserPassword(LdapUtils.asciiToString(person.getUserPassword()))); | |||
| return Result.success(personList); | |||
| } | |||
| /** | |||
| * 保存 | |||
| * | |||
| * @param person {@link Person} | |||
| */ | |||
| @Override | |||
| public void save(Person person) { | |||
| Person p = personRepository.save(person); | |||
| log.info("用户{}保存成功", p.getUid()); | |||
| } | |||
| /** | |||
| * 删除 | |||
| * | |||
| * @param person {@link Person} | |||
| */ | |||
| @Override | |||
| public void delete(Person person) { | |||
| personRepository.delete(person); | |||
| log.info("删除用户{}成功", person.getUid()); | |||
| } | |||
| } | |||
| ``` | |||
| ## LdapDemoApplicationTests.java | |||
| > 测试 | |||
| ``` | |||
| package com.xkcoding.ldap; | |||
| import com.xkcoding.ldap.entity.Person; | |||
| import com.xkcoding.ldap.entity.Result; | |||
| import com.xkcoding.ldap.request.LoginRequest; | |||
| import com.xkcoding.ldap.service.PersonService; | |||
| import org.junit.Test; | |||
| import org.junit.runner.RunWith; | |||
| import org.springframework.boot.test.context.SpringBootTest; | |||
| import org.springframework.test.context.junit4.SpringRunner; | |||
| import javax.annotation.Resource; | |||
| ```java | |||
| /** | |||
| * LdapDemoApplicationTest | |||
| * | |||
| @@ -380,6 +330,9 @@ public class LdapDemoApplicationTests { | |||
| public void contextLoads() { | |||
| } | |||
| /** | |||
| * 测试查询单个 | |||
| */ | |||
| @Test | |||
| public void loginTest() { | |||
| LoginRequest loginRequest = LoginRequest.builder().username("wangwu").password("123456").build(); | |||
| @@ -387,12 +340,18 @@ public class LdapDemoApplicationTests { | |||
| System.out.println(login); | |||
| } | |||
| /** | |||
| * 测试查询列表 | |||
| */ | |||
| @Test | |||
| public void listAllPersonTest() { | |||
| Result result = personService.listAllPerson(); | |||
| System.out.println(result); | |||
| } | |||
| /** | |||
| * 测试保存 | |||
| */ | |||
| @Test | |||
| public void saveTest() { | |||
| Person person = new Person(); | |||
| @@ -413,7 +372,9 @@ public class LdapDemoApplicationTests { | |||
| personService.save(person); | |||
| } | |||
| /** | |||
| * 测试删除 | |||
| */ | |||
| @Test | |||
| public void deleteTest() { | |||
| Person person = new Person(); | |||
| @@ -421,8 +382,12 @@ public class LdapDemoApplicationTests { | |||
| personService.delete(person); | |||
| } | |||
| } | |||
| ``` | |||
| ## 其余代码参见本 demo | |||
| ## 参考 | |||
| spring-data-ldap 官方文档: https://docs.spring.io/spring-data/ldap/docs/2.1.10.RELEASE/reference/html/ | |||
| @@ -1,4 +1,4 @@ | |||
| package com.xkcoding.ldap.entity; | |||
| package com.xkcoding.ldap.api; | |||
| import lombok.Data; | |||
| import org.springframework.lang.Nullable; | |||
| @@ -1,4 +1,4 @@ | |||
| package com.xkcoding.ldap.entity; | |||
| package com.xkcoding.ldap.api; | |||
| import lombok.AllArgsConstructor; | |||
| import lombok.Getter; | |||
| @@ -1,6 +1,6 @@ | |||
| package com.xkcoding.ldap.exception; | |||
| import com.xkcoding.ldap.entity.ResultCode; | |||
| import com.xkcoding.ldap.api.ResultCode; | |||
| import lombok.Getter; | |||
| /** | |||
| @@ -15,7 +15,6 @@ public class ServiceException extends RuntimeException { | |||
| @Getter | |||
| private int errcode; | |||
| @SuppressWarnings("NullableProblems") | |||
| @Getter | |||
| private String errmsg; | |||
| @@ -23,27 +23,4 @@ public interface PersonRepository extends CrudRepository<Person, Name> { | |||
| * @return com.xkcoding.ldap.entity.Person | |||
| */ | |||
| Person findByUid(String uid); | |||
| /** | |||
| * 查询全部 | |||
| * @return | |||
| */ | |||
| @Override | |||
| Iterable<Person> findAll(); | |||
| /** | |||
| * 保存 | |||
| * @param s | |||
| * @param <S> | |||
| * @return | |||
| */ | |||
| @Override | |||
| <S extends Person> S save(S s); | |||
| /** | |||
| * 删除 | |||
| * @param person | |||
| */ | |||
| @Override | |||
| void delete(Person person); | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| package com.xkcoding.ldap.service; | |||
| import com.xkcoding.ldap.api.Result; | |||
| import com.xkcoding.ldap.entity.Person; | |||
| import com.xkcoding.ldap.entity.Result; | |||
| import com.xkcoding.ldap.request.LoginRequest; | |||
| /** | |||
| @@ -15,26 +15,30 @@ public interface PersonService { | |||
| /** | |||
| * 登录 | |||
| * @param request com.xkcoding.ldap.request.LoginRequest | |||
| * @return com.xkcoding.ldap.entity.Result | |||
| * | |||
| * @param request {@link LoginRequest} | |||
| * @return {@link Result} | |||
| */ | |||
| Result login(LoginRequest request); | |||
| /** | |||
| * 查询全部 | |||
| * @return com.xkcoding.ldap.entity.Result | |||
| * | |||
| * @return {@link Result} | |||
| */ | |||
| Result listAllPerson(); | |||
| /** | |||
| * 保存 | |||
| * @param person com.xkcoding.ldap.entity.Person | |||
| * | |||
| * @param person {@link Person} | |||
| */ | |||
| void save(Person person); | |||
| /** | |||
| * 删除 | |||
| * @param person com.xkcoding.ldap.entity.Person | |||
| * | |||
| * @param person {@link Person} | |||
| */ | |||
| void delete(Person person); | |||
| @@ -1,20 +1,19 @@ | |||
| package com.xkcoding.ldap.service.impl; | |||
| import com.xkcoding.ldap.api.Result; | |||
| import com.xkcoding.ldap.entity.Person; | |||
| import com.xkcoding.ldap.entity.Result; | |||
| import com.xkcoding.ldap.exception.ServiceException; | |||
| import com.xkcoding.ldap.repository.PersonRepository; | |||
| import com.xkcoding.ldap.request.LoginRequest; | |||
| import com.xkcoding.ldap.service.PersonService; | |||
| import com.xkcoding.ldap.util.LdapUtils; | |||
| import lombok.RequiredArgsConstructor; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Service; | |||
| import org.springframework.util.ObjectUtils; | |||
| import org.springframework.util.StringUtils; | |||
| import javax.annotation.Resource; | |||
| import java.security.NoSuchAlgorithmException; | |||
| import java.util.List; | |||
| /** | |||
| * PersonServiceImpl | |||
| @@ -25,20 +24,24 @@ import java.util.List; | |||
| */ | |||
| @Slf4j | |||
| @Service | |||
| @RequiredArgsConstructor(onConstructor_ = @Autowired) | |||
| public class PersonServiceImpl implements PersonService { | |||
| private final PersonRepository personRepository; | |||
| @Resource | |||
| private PersonRepository personRepository; | |||
| /** | |||
| * 登录 | |||
| * | |||
| * @param request {@link LoginRequest} | |||
| * @return {@link Result} | |||
| */ | |||
| @Override | |||
| public Result login(LoginRequest request) { | |||
| log.info("IN LDAP auth"); | |||
| Person user = personRepository.findByUid(request.getUsername()); | |||
| try { | |||
| if(ObjectUtils.isEmpty(user)) { | |||
| if (ObjectUtils.isEmpty(user)) { | |||
| throw new ServiceException("用户名或密码错误,请重新尝试"); | |||
| } else { | |||
| user.setUserPassword(LdapUtils.asciiToString(user.getUserPassword())); | |||
| @@ -54,24 +57,38 @@ public class PersonServiceImpl implements PersonService { | |||
| return Result.success(user); | |||
| } | |||
| /** | |||
| * 查询全部 | |||
| * | |||
| * @return {@link Result} | |||
| */ | |||
| @Override | |||
| public Result listAllPerson() { | |||
| Iterable<Person> personList = personRepository.findAll(); | |||
| personList.forEach(person -> { | |||
| person.setUserPassword(LdapUtils.asciiToString(person.getUserPassword())); | |||
| }); | |||
| personList.forEach(person -> person.setUserPassword(LdapUtils.asciiToString(person.getUserPassword()))); | |||
| return Result.success(personList); | |||
| } | |||
| /** | |||
| * 保存 | |||
| * | |||
| * @param person {@link Person} | |||
| */ | |||
| @Override | |||
| public void save(Person person) { | |||
| Person p = personRepository.save(person); | |||
| log.info("用户{}保存成功", p.getUid()); | |||
| } | |||
| /** | |||
| * 删除 | |||
| * | |||
| * @param person {@link Person} | |||
| */ | |||
| @Override | |||
| public void delete(Person person) { | |||
| personRepository.delete(person); | |||
| log.info("删除用户{}成功", person.getUid()); | |||
| } | |||
| } | |||
| @@ -16,54 +16,55 @@ public class LdapUtils { | |||
| /** | |||
| * 校验密码 | |||
| * @param ldappw ldap 加密密码 | |||
| * @param inputpw 用户输入 | |||
| * | |||
| * @param ldapPassword ldap 加密密码 | |||
| * @param inputPassword 用户输入 | |||
| * @return boolean | |||
| * @throws NoSuchAlgorithmException | |||
| * @throws NoSuchAlgorithmException 加解密异常 | |||
| */ | |||
| public static boolean verify(String ldappw, String inputpw) | |||
| throws NoSuchAlgorithmException { | |||
| public static boolean verify(String ldapPassword, String inputPassword) throws NoSuchAlgorithmException { | |||
| // MessageDigest 提供了消息摘要算法,如 MD5 或 SHA,的功能,这里LDAP使用的是SHA-1 | |||
| MessageDigest md = MessageDigest.getInstance("SHA-1"); | |||
| // 取出加密字符 | |||
| if (ldappw.startsWith("{SSHA}")) { | |||
| ldappw = ldappw.substring(6); | |||
| } else if (ldappw.startsWith("{SHA}")) { | |||
| ldappw = ldappw.substring(5); | |||
| if (ldapPassword.startsWith("{SSHA}")) { | |||
| ldapPassword = ldapPassword.substring(6); | |||
| } else if (ldapPassword.startsWith("{SHA}")) { | |||
| ldapPassword = ldapPassword.substring(5); | |||
| } | |||
| // 解码BASE64 | |||
| byte[] ldappwbyte = Base64.decode(ldappw); | |||
| byte[] shacode; | |||
| byte[] ldapPasswordByte = Base64.decode(ldapPassword); | |||
| byte[] shaCode; | |||
| byte[] salt; | |||
| // 前20位是SHA-1加密段,20位后是最初加密时的随机明文 | |||
| if (ldappwbyte.length <= 20) { | |||
| shacode = ldappwbyte; | |||
| if (ldapPasswordByte.length <= 20) { | |||
| shaCode = ldapPasswordByte; | |||
| salt = new byte[0]; | |||
| } else { | |||
| shacode = new byte[20]; | |||
| salt = new byte[ldappwbyte.length - 20]; | |||
| System.arraycopy(ldappwbyte, 0, shacode, 0, 20); | |||
| System.arraycopy(ldappwbyte, 20, salt, 0, salt.length); | |||
| shaCode = new byte[20]; | |||
| salt = new byte[ldapPasswordByte.length - 20]; | |||
| System.arraycopy(ldapPasswordByte, 0, shaCode, 0, 20); | |||
| System.arraycopy(ldapPasswordByte, 20, salt, 0, salt.length); | |||
| } | |||
| // 把用户输入的密码添加到摘要计算信息 | |||
| md.update(inputpw.getBytes()); | |||
| md.update(inputPassword.getBytes()); | |||
| // 把随机明文添加到摘要计算信息 | |||
| md.update(salt); | |||
| // 按SSHA把当前用户密码进行计算 | |||
| byte[] inputpwbyte = md.digest(); | |||
| byte[] inputPasswordByte = md.digest(); | |||
| // 返回校验结果 | |||
| return MessageDigest.isEqual(shacode, inputpwbyte); | |||
| return MessageDigest.isEqual(shaCode, inputPasswordByte); | |||
| } | |||
| /** | |||
| * Ascii转换为字符串 | |||
| * @param value | |||
| * @return | |||
| * | |||
| * @param value Ascii串 | |||
| * @return 字符串 | |||
| */ | |||
| public static String asciiToString(String value) { | |||
| StringBuilder sbu = new StringBuilder(); | |||
| @@ -1,7 +1,7 @@ | |||
| package com.xkcoding.ldap; | |||
| import com.xkcoding.ldap.api.Result; | |||
| import com.xkcoding.ldap.entity.Person; | |||
| import com.xkcoding.ldap.entity.Result; | |||
| import com.xkcoding.ldap.request.LoginRequest; | |||
| import com.xkcoding.ldap.service.PersonService; | |||
| import org.junit.Test; | |||
| @@ -29,6 +29,9 @@ public class LdapDemoApplicationTests { | |||
| public void contextLoads() { | |||
| } | |||
| /** | |||
| * 测试查询单个 | |||
| */ | |||
| @Test | |||
| public void loginTest() { | |||
| LoginRequest loginRequest = LoginRequest.builder().username("wangwu").password("123456").build(); | |||
| @@ -36,12 +39,18 @@ public class LdapDemoApplicationTests { | |||
| System.out.println(login); | |||
| } | |||
| /** | |||
| * 测试查询列表 | |||
| */ | |||
| @Test | |||
| public void listAllPersonTest() { | |||
| Result result = personService.listAllPerson(); | |||
| System.out.println(result); | |||
| } | |||
| /** | |||
| * 测试保存 | |||
| */ | |||
| @Test | |||
| public void saveTest() { | |||
| Person person = new Person(); | |||
| @@ -62,7 +71,9 @@ public class LdapDemoApplicationTests { | |||
| personService.save(person); | |||
| } | |||
| /** | |||
| * 测试删除 | |||
| */ | |||
| @Test | |||
| public void deleteTest() { | |||
| Person person = new Person(); | |||
| @@ -71,7 +82,4 @@ public class LdapDemoApplicationTests { | |||
| personService.delete(person); | |||
| } | |||
| } | |||