Implementing User Authentication with LDAP in Spring Boot
1. Add spring-boot-starter-data-ldap Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
2. Configuration File
spring:
ldap:
urls: ldap://xx.xx.xx.xx:389
base: OU=xxx,DC=xxx,DC=com
username: xxx@xx.com
password: xxx
Fill in the URL, baseDN, username, and password information in the configuration file
3. Create User Object Class
import com.alibaba.fastjson.annotation.JSONField;
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;
/**
* LDAP protocol person class
*/
@Data
@Entry(objectClasses = "person")
public class Person {
/**
* Unique identifier
*/
@Id
@JSONField(serialize = false)
private Name distinguishedName;
/**
* Login account
*/
@Attribute(name = "sAMAccountName")
private String loginName;
/**
* User name
*/
@Attribute(name = "cn")
private String name;
/**
* Permission code
*/
@Attribute(name = "userAccountControl")
private Integer userAccountControl;
/**
* Is deleted
*/
private Boolean isDelete;
}
4. User Login
4.1 Example Code
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.EqualsFilter;
private final LdapTemplate ldapTemplate;
private static final String LDAP_USERNAME_ATTR = "sAMAccountName";
public void login(String userName, String password) {
EqualsFilter filter = new EqualsFilter(LDAP_USERNAME_ATTR, userName);
boolean result = ldapTemplate.authenticate("", filter.toString(), password);
}
The basic logic is to call the ldapTemplate’s authenticate method for authentication.
4.2 Complete Code
package com.cowave.meter.admin.user.service.impl;
import cn.hutool.core.util.IdUtil;
import com.cowave.commons.framework.access.Access;
import com.cowave.commons.framework.filter.security.AccessToken;
import com.cowave.commons.framework.filter.security.TokenService;
import com.cowave.commons.framework.support.mybatis.page.PageDO;
import com.cowave.commons.framework.util.Asserts;
import com.cowave.commons.framework.util.AssertsException;
import com.cowave.meter.admin.user.dao.SysDeptDao;
import com.cowave.meter.admin.user.dao.SysRoleDao;
import com.cowave.meter.admin.user.dao.SysUserDao;
import com.cowave.meter.admin.user.pojo.Person;
import com.cowave.meter.admin.user.pojo.SysDept;
import com.cowave.meter.admin.user.pojo.SysRole;
import com.cowave.meter.admin.user.pojo.SysUser;
import com.cowave.meter.admin.user.service.LdapService;
import com.cowave.meter.admin.user.service.SysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class LdapServiceImpl implements LdapService {
private final LdapTemplate ldapTemplate;
private final TokenService tokenService;
private final SysUserDao sysUserDao;
private final SysUserService userService;
private final SysDeptDao sysDeptDao;
private final SysRoleDao sysRoleDao;
private final BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
private static final String LDAP_USERNAME_ATTR = "sAMAccountName";
/**
* Domain account login
*
* @param userName
* @param password
* @return
*/
@Override
public AccessToken domainLogin(String userName, String password) {
EqualsFilter filter = new EqualsFilter(LDAP_USERNAME_ATTR, userName);
boolean authenticate = ldapTemplate.authenticate("", filter.toString(), password);
if (!authenticate) {
throw new AssertsException("auth.failed");
}
Boolean domainUserAccountExist = sysUserDao.domainUserAccountExist(userName);
if (Boolean.FALSE.equals(domainUserAccountExist)) {
LdapQueryBuilder ldapQuery = LdapQueryBuilder.query();
ldapQuery.filter(filter);
Person person = ldapTemplate.findOne(ldapQuery, Person.class);
if (ObjectUtils.isEmpty(person)) {
throw new AssertsException("user.notexist");
}
SysUser sysUser = new SysUser();
sysUser.setUserAccount(userName);
Asserts.isFalse(userService.userAccountExist(sysUser), "Account name already exists");
sysUser.setUserName(person.getName());
sysUser.setUserPasswd(bcryptPasswordEncoder.encode(password));
sysUser.setDomainUserAccount(userName);
// Set department
SysDept sysDept = sysDeptDao.queryRootDept();
if (!ObjectUtils.isEmpty(sysDept)) {
sysUser.setDeptId(sysDept.getDeptId());
}
// Set role
PageDO<SysRole> sysRolePageDO = sysRoleDao.queryPage(null, "2", 1, 1);
if (sysRolePageDO.getPages() > 0) {
SysRole sysRole = sysRolePageDO.getList().get(0);
sysUser.setRoleId(sysRole.getRoleId());
}
userService.saveOrUpdate(sysUser);
}
SysUser sysUser = sysUserDao.getByDomainUserAccount(userName);
String userId = sysUser.getUserId();
AccessToken accessToken = new AccessToken();
accessToken.setType(AccessToken.TYPE_USER);
accessToken.setUserCode(userId);
accessToken.setUsername(sysUser.getUserAccount());
accessToken.setUserNick(sysUser.getUserName());
accessToken.setDeptCode(sysUser.getDeptId());
accessToken.setRoles(List.of(sysUser.getRoleId()));
accessToken.setLoginIp(Access.ip());
accessToken.setLoginTime(Access.time());
accessToken.setAccessIp(Access.ip());
accessToken.setAccessTime(Access.time());
accessToken.setId(IdUtil.fastSimpleUUID());
String token = tokenService.newToken(accessToken);
accessToken.setToken(token);
return accessToken;
}
}
4.3 Check if User is Disabled
/**
* Determine whether the user is disabled based on AD domain's userAccountControl attribute
*
* @param userAccContr
* @return
*/
private Boolean getUserDelete(int userAccContr) {
//TRUSTED_TO_AUTH_FOR_DELEGATION - Allow this account to be delegated
if (userAccContr >= 16777216) {
userAccContr = userAccContr - 16777216;
}
//PASSWORD_EXPIRED - (Windows 2000/Windows Server 2003) User's password has expired
if (userAccContr >= 8388608) {
userAccContr = userAccContr - 8388608;
}
//DONT_REQ_PREAUTH
if (userAccContr >= 4194304) {
userAccContr = userAccContr - 4194304;
}
//USE_DES_KEY_ONLY - (Windows 2000/Windows Server 2003) Restrict this user to use only Data Encryption Standard (DES) encryption type keys
if (userAccContr >= 2097152) {
userAccContr = userAccContr - 2097152;
}
//NOT_DELEGATED - When this flag is set, the user's security context will not be delegated to the service even if the service account is trusted for Kerberos delegation
if (userAccContr >= 1048576) {
userAccContr = userAccContr - 1048576;
}
//TRUSTED_FOR_DELEGATION - When this flag is set, the service account (user or computer account) running the service is trusted for Kerberos delegation.
// Any such service can impersonate the client requesting the service. To allow a service for Kerberos delegation, this flag must be set on the service account's userAccountControl attribute
if (userAccContr >= 524288) {
userAccContr = userAccContr - 524288;
}
//SMARTCARD_REQUIRED - When this flag is set, the user will be forced to use a smart card to log in
if (userAccContr >= 262144) {
userAccContr = userAccContr - 262144;
}
//MNS_LOGON_ACCOUNT - This is an MNS logon account
if (userAccContr >= 131072) {
userAccContr = userAccContr - 131072;
}
//DONT_EXPIRE_PASSWORD - Password never expires
if (userAccContr >= 65536) {
userAccContr = userAccContr - 65536;
}
//MNS_LOGON_ACCOUNT - This is an MNS logon account
if (userAccContr >= 2097152) {
userAccContr = userAccContr - 2097152;
}
//SERVER_TRUST_ACCOUNT - This is a computer account for a domain controller that belongs to this domain
if (userAccContr >= 8192) {
userAccContr = userAccContr - 8192;
}
//WORKSTATION_TRUST_ACCOUNT - This is a computer account for a computer running Microsoft Windows NT 4.0 Workstation, Microsoft Windows NT 4.0 Server,
// Microsoft Windows 2000 Professional, or Windows 2000 Server that belongs to this domain
if (userAccContr >= 4096) {
userAccContr = userAccContr - 4096;
}
//INTERDOMAIN_TRUST_ACCOUNT - For system domains that trust other domains, this attribute allows accounts that trust this system domain
if (userAccContr >= 2048) {
userAccContr = userAccContr - 2048;
}
//NORMAL_ACCOUNT - This is the default account type representing a typical user
if (userAccContr >= 512) {
userAccContr = userAccContr - 512;
}
//TEMP_DUPLICATE_ACCOUNT - This account belongs to a user whose primary account is in another domain. This account provides the user with access to this domain,
// but not access to any domains that trust this domain. Sometimes this account is called a "local user account"
if (userAccContr >= 256) {
userAccContr = userAccContr - 256;
}
//ENCRYPTED_TEXT_PASSWORD_ALLOWED - User can send encrypted passwords
if (userAccContr >= 128) {
userAccContr = userAccContr - 128;
}
//PASSWD_CANT_CHANGE - User cannot change password. This flag can be read but cannot be set directly
if (userAccContr >= 64) {
userAccContr = userAccContr - 64;
}
//PASSWD_NOTREQD - Password not required
if (userAccContr >= 32) {
userAccContr = userAccContr - 32;
}
//LOCKOUT
if (userAccContr >= 16) {
userAccContr = userAccContr - 16;
}
//HOMEDIR_REQUIRED - Home folder required
if (userAccContr >= 8) {
userAccContr = userAccContr - 8;
}
if (userAccContr >= 2) {
return true;
}
return false;
}