@@ -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); | |||
} | |||
} |