看了网上很多资料,大多是用于查询认证。增删改很少,特在此整理一下。供大家学习。
注:前置条件,ad域证书已经导入到jdk(大家可以查看我另一篇关于ad证书导入到jdk中)
1.idea创建springboot项目我就不细说了,先导入idap依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
2.application.yml文件配置idap基础信息
spring:
# LDAP连接配置
ldap:
urls: ldaps://ip:636/
base: OU=xxx,OU=xxx,DC=xxx,DC=xxx
username: xxx
password: xxx
注:636端口是ssl连接,需要证书导入,便于修改密码。如果只做用户认证端口用389即可
3.创建实体类
package com.example.idapoperation.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModelProperty;
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;
@Entry(objectClasses = {"user", "organizationalPerson","top","Person"})
@Data
public class LdapUser {
@Id
@JsonIgnore // 必写
private Name dn;
@Attribute(name = "distinguishedName")
@ApiModelProperty(value = "识别名", name = "dn")
private String distinguishedName;
/**
* 登录账号
*/
@Attribute(name = "sAMAccountName")
@ApiModelProperty(value = "登录账号", required = true, name = "loginName")
private String loginName;
/**
* 正式名称,即用户姓
*/
@Attribute(name = "cn")
@ApiModelProperty(value = "正式名称,AD域属性值cn,需唯一,例如用工号", name = "userName")
private String userName;
/**
* 姓
*/
@Attribute(name = "sn")
@ApiModelProperty(value = "姓", required = true, name = "sn")
private String sn;
/**
* 名
*/
@Attribute(name = "givenname")
@ApiModelProperty(value = "名", required = true, name = "givenName")
private String givenName;
/**
* 显示名称
*/
@Attribute(name = "displayName ")
@ApiModelProperty(value = "显示名称", required = true, name = "displayName")
private String displayName;
/**
* 邮箱
*/
@Attribute(name = "mail")
@ApiModelProperty(value = "邮箱", required = true, name = "email")
private String email;
@Attribute(name = "userAccountControl")
@ApiModelProperty(value = "用户属性", name = "userAccountControl")
private String userAccountControl;
}
注:@ApiModelProperty:这个注解是swagger2的
Name dn:相当于唯一标识
LDAP中,一个条目(Entry)必须包含一个对象类(objectClass)属性,且需要赋予至少一个值。每一个值将用作一条LDAP条目进行数据存储的模板;模板中包含了一个条目必须被赋值的属性和可选的属性。
objectClass有着严格的等级之分,最顶层是top和alias。例如,organizationalPerson这个objectClass就隶属于person,而person又隶属于top。
objectClass可分为以下3类:
结构型(Structural):如account、inetOrgPerson、person和organizationUnit;
辅助型(Auxiliary):如extensibeObject;
抽象型(Abstract):如top,抽象型的objectClass不能直接使用。
每种objectClass有自己的数据结构,比如我们有一种叫“电话薄”的objectClass,肯定会内置很多属性(attributes),如姓名(uid),身份证号(uidNumber),单位名称(gid),家庭地址(homeDirectory)等,这些属性(attributes)中,有些是必填的,例如,account就要求userid是必填项,而inetOrgPerson则要求cn(common name,常用名称)和sn(sure name,真实名称)是必填项。
4.创建service层
package com.example.idapoperation.service;
import com.example.idapoperation.model.LdapUser;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public interface ILdapService {
/**
* LDAP用户认证
*/
boolean authenticate(String loginName, String password);
/**
* 检索域用户
*/
List<LdapUser> searchLdapUser(String keyword);
/**
* 修改密码
*/
void resetPwd(String loginName, String newPassword) throws Exception;
/**
* 新增用户
*
* @param user
* @return
*/
void addUser(LdapUser user) throws Exception;
/**
* 根据cn删除
*
* @param cn
*/
void deleteUser(String cn);
/**
* 根据cn修改用户属性
* @param cn
*/
void updateUserAttr(String cn);
/**
* 查询所有
* @return
*/
List<LdapUser> getAll();
}
5.创建实现类
package com.example.idapoperation.service.impl;
import com.example.idapoperation.common.Constant;
import com.example.idapoperation.model.LdapUser;
import com.example.idapoperation.service.ILdapService;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.support.LdapNameBuilder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.naming.directory.*;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
@Service("ILdapService")
public class LdapServiceImpl implements ILdapService {
@Resource
private LdapTemplate ldapTemplate;
/**
* LDAP用户认证
*
* @param loginName
* @param password
* @return
*/
@Override
public boolean authenticate(String loginName, String password) {
EqualsFilter filter = new EqualsFilter("sAMAccountName", loginName);
return ldapTemplate.authenticate("", filter.toString(), password);
}
/**
* 检索域用户(根据用户登录名、正式名称、邮箱,模糊搜索)
* <p>
* sAMAccountName:用户登录名(Windows 2000以前版本,以后的版本是userPrincipalName)
* sn:姓;cn:正式名称;mail:邮箱;ou:组织单位;dn:识别名;
*
* @param keyword
* @return
*/
@Override
public List<LdapUser> searchLdapUser(String keyword) {
keyword = "*" + keyword + "*";
LdapQuery query = query().where("sAMAccountName").like(keyword).or("cn").like(keyword).or("mail").like(keyword);
return ldapTemplate.find(query, LdapUser.class);
}
/**
* 重置密码
*
* @param loginName
* @param newPassword
* @throws Exception
*/
@Override
public void resetPwd(String loginName, String newPassword) throws Exception {
// 1. 查找AD用户
LdapQuery query = query().where("sAMAccountName").is(loginName);
LdapUser person = ldapTemplate.findOne(query, LdapUser.class);
// 2. 创建密码
String newQuotedPassword = "\"" + newPassword + "\"";
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
// 3. 修改密码
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
ldapTemplate.modifyAttributes(person.getDn(), new ModificationItem[]{item});
}
/**
* 新增用户
*
* @param user
* @return
*/
@Override
public void addUser(LdapUser user) {
// 基类设置
BasicAttribute ocattr = new BasicAttribute("objectClass");
ocattr.add("top");
ocattr.add("person");
ocattr.add("organizationalPerson");
ocattr.add("user");
// 用户属性
Attributes attrs = new BasicAttributes();
attrs.put(ocattr);
//用户登录名
attrs.put("userPrincipalName", user.getLoginName());
//用户登录名 windows 2000以前的版本
attrs.put("sAMAccountName", user.getLoginName());
//用户正式名
attrs.put("cn", user.getUserName());
//姓
attrs.put("sn", user.getSn());
//名
attrs.put("givenname", user.getGivenName());
//显示名称
attrs.put("displayName", user.getDisplayName());
//邮件
attrs.put("mail", user.getEmail());
//下次登录修改密码
attrs.put("pwdLastSet", "0");
//启用账户
attrs.put("userAccountControl","544");
//密码
attrs.put("userPassword", Constant.DEFAULT_PWD);
ldapTemplate.bind(LdapNameBuilder.newInstance().add("CN", user.getUserName()).build(), null, attrs);
}
/**
* 删除用户
*
* @param cn
*/
@Override
public void deleteUser(String cn) {
ldapTemplate.unbind(LdapNameBuilder.newInstance().add("CN", cn).build());
}
@Override
public List<LdapUser> getAll() {
return ldapTemplate.findAll(LdapUser.class);
}
}
注:
1.userAccountControl:544(启用账户,且账户永不过期)。如果不设置,创建的账户就是禁用状态
2.ldapTemplate.bind()方法,第一个参数是Name dn,dn一般由cn+ou等组成(cn是正式名称,ou是组织单位)想了解更多可以查下ad域属性详解。我这里只用cn做唯一标识
3.如果要修改多个属性,可以创建ModificationItem[] mArray = new ModificationItem[10]数组完成
6.创建controller
package com.example.idapoperation.controller;
import com.example.idapoperation.common.MyException;
import com.example.idapoperation.common.Result;
import com.example.idapoperation.enums.ExceptionEnum;
import com.example.idapoperation.model.LdapUser;
import com.example.idapoperation.service.ILdapService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@RequestMapping("/idap")
@Api(tags = "AD域管理")
public class IdapController {
@Autowired
private ILdapService iLdapService;
@ApiOperation("用户认证")
@PostMapping("/authUser")
@ResponseBody
@ApiImplicitParams({
@ApiImplicitParam(name = "userName", value = "账号", required = true, dataType = "String"),
@ApiImplicitParam(name = "password", value = "密码", required = true, dataType = "String")
})
public Result authUser(@RequestParam String userName, @RequestParam String password) {
Boolean flag = iLdapService.authenticate(userName, password);
if (!flag) {
throw new MyException(ExceptionEnum.AUTH_ERROR.getCode(), ExceptionEnum.AUTH_ERROR.getMsg());
}
return Result.success();
}
@ApiOperation("根据用户名、正式名称、邮箱检索用户")
@PostMapping("/searchUser")
@ResponseBody
@ApiImplicitParams({
@ApiImplicitParam(name = "keyword", value = "参数可以是用户名、正式名称、邮箱都可以", required = true, dataType = "String")
})
public Result searchUser(@RequestParam String keyword) {
List<LdapUser> list = iLdapService.searchLdapUser(keyword);
return Result.success(list);
}
@ApiOperation("重置密码")
@PostMapping("/resetPwd")
@ResponseBody
@ApiImplicitParams({
@ApiImplicitParam(name = "loginName", value = "用户登录名", required = true, dataType = "String"),
@ApiImplicitParam(name = "newPwd", value = "新密码", required = true, dataType = "String")
})
public Result resetPwd(@RequestParam String loginName, @RequestParam String newPwd) {
try {
iLdapService.resetPwd(loginName, newPwd);
} catch (Exception e) {
throw new MyException(ExceptionEnum.RESETPWD_ERROR.getCode(), ExceptionEnum.RESETPWD_ERROR.getMsg());
}
return Result.success();
}
@ApiOperation("新增用户")
@PostMapping("/addUser")
@ResponseBody
public Result addUser(@RequestBody LdapUser ldapUser) {
try {
iLdapService.addUser(ldapUser);
// iLdapService.updateUserAttr(ldapUser.getUserName());
} catch (Exception e) {
e.printStackTrace();
throw new MyException(ExceptionEnum.ADD_ERROR.getCode(), ExceptionEnum.ADD_ERROR.getMsg());
}
return Result.success();
}
/**
* @param cn
* @return
*/
@ApiOperation("删除用户")
@PostMapping("/deleteUser")
@ResponseBody
@ApiImplicitParams({
@ApiImplicitParam(name = "cn", value = "工号", required = true, dataType = "String")
})
public Result deleteUser(@RequestParam(name = "cn") String cn) {
try {
iLdapService.deleteUser(cn);
} catch (Exception e) {
e.printStackTrace();
throw new MyException(ExceptionEnum.DELETE_ERROR.getCode(), ExceptionEnum.DELETE_ERROR.getMsg());
}
return Result.success();
}
@ApiOperation("查询所有用户")
@PostMapping("/findAll")
@ResponseBody
public Result findAll() {
List<LdapUser> list = iLdapService.getAll();
return Result.success(list);
}
注:throw new MyException()是我自己封装的统一异常处理。大家可以根据的实际情况修改