看了网上很多资料,大多是用于查询认证。增删改很少,特在此整理一下。供大家学习。

注:前置条件,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()是我自己封装的统一异常处理。大家可以根据的实际情况修改