1.对AD域操作的一个工具类
import com.ruoyi.system.api.domain.SysUser;
import org.apache.commons.compress.utils.Lists;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import javax.naming.ldap.InitialLdapContext;
import java.util.List;
import java.util.Properties;
/**
* @Description:对AD域用户的增删改查操作
* @author moonxy
* @date 2018-05-15
*/
public class ADUserUtils{
DirContextdc =null;
public static final Stringroot ="OU=xxxxx,DC=xxxxx,DC=COM"; // LDAP的根节点的DC
/**
* @Description:程序入口
* @author moonxy
* @date 2018-05-15
*/
// public static void main(String[] args) {
// ADUserUtils utils = new ADUserUtils();
//
//// utils.add("JimGreen");
//
//// SearchResult sr = utils.searchByUserName(utils.root, "JimGreen");
//// System.out.println(sr.getName());
////
//// utils.modifyInformation(sr.getName(), "M1380005");
//
// utils.searchInformation(utils.root);
//
//// utils.renameEntry("CN=JimGreen,OU=Java开发组,OU=软件研发部,DC=moonxy,DC=com", "CN=JimGreen,OU=Web前端组,OU=软件研发部,DC=moonxy,DC=com");
//
//// utils.delete("CN=JimGreen,OU=Web前端组,OU=软件研发部,DC=moonxy,DC=com");
//
// utils.close();
// }
/**
* 初始化
*/
public ADUserUtils() {
super();
init();
}
/**
* @Description:初始化AD域服务连接
* @date 2018-05-15
*/
public void init() {
Properties env =new Properties();
String adminName ="xxxx";//username@domain
String adminPassword ="xxxx";//password
String ldapURL ="ldap://ip:port";//ip:port
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");//LDAP访问安全级别:"none","simple","strong"
env.put(Context.SECURITY_PRINCIPAL, adminName);
env.put(Context.SECURITY_CREDENTIALS, adminPassword);
env.put(Context.PROVIDER_URL, ldapURL);
env.put("com.sun.jndi.ldap.connect.timeout", "3000");
try {
dc =new InitialLdapContext(env, null);
System.out.println("AD域服务连接认证成功");
} catch (Exception e) {
System.out.println("AD域服务连接认证失败");
e.printStackTrace();
}
}
/**
* @Description:关闭AD域服务连接
* @author moonxy
* @date 2018-05-15
*/
public void close() {
if (dc !=null) {
try {
dc.close();
} catch (NamingException e) {
System.out.println("NamingException in close():" + e);
}
}
}
/**
* @Description:新增AD域用户
* @author moonxy
* @date 2018-05-15
*/
public void add(String newUserName) {
try {
Attributes attrs =new BasicAttributes(true);
attrs.put("objectClass", "user");
attrs.put("samAccountName", newUserName);
attrs.put("displayName", newUserName);
attrs.put("userPrincipalName", newUserName +"@moonxy.com");
dc.createSubcontext("CN=" + newUserName +"," +root, attrs);
System.out.println("新增AD域用户成功:" + newUserName);
} catch (Exception e) {
e.printStackTrace();
System.out.println("新增AD域用户失败:" + newUserName);
}
}
/**
* @Description:删除AD域用户
* @author moonxy
* @date 2018-05-15
*/
public void delete(String dn) {
try {
dc.destroySubcontext(dn);
System.out.println("删除AD域用户成功:" + dn);
} catch (Exception e) {
System.out.println("删除AD域用户失败:" + dn);
e.printStackTrace();
}
}
/**
* @Description:重命名AD域用户
* @author moonxy
* @date 2018-05-15
*/
public boolean renameEntry(String oldDN, String newDN) {
try {
dc.rename(oldDN, newDN);
System.out.println("重命名AD域用户成功");
return true;
} catch (NamingException ne) {
System.out.println("重命名AD域用户失败");
ne.printStackTrace();
return false;
}
}
/**
* @Description:修改AD域用户属性
* @author moonxy
* @date 2018-05-15
*/
public boolean modifyInformation(String dn, String fieldValue) {
try {
ModificationItem[] mods =new ModificationItem[1];
// 修改属性
Attribute attr0 =new BasicAttribute("homePhone",fieldValue);
//mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr0);//新增属性
//mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,attr0);//删除属性
mods[0] =new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr0);//覆盖属性
dc.modifyAttributes(dn +"," +root, mods);
System.out.println("修改AD域用户属性成功");
return true;
} catch (Exception e) {
System.err.println("修改AD域用户属性失败");
e.printStackTrace();
return false;
}
}
/**
* @Description:搜索指定节点下的所有AD域用户
* @author moonxy
* @date 2018-05-15
*/
public List<SysUser> searchInformation(String searchBase,String searchFilter,String[] returnedAtts) {
List<SysUser> userList = Lists.newArrayList();
try {
SearchControls searchCtls =new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchCtls.setReturningAttributes(returnedAtts);
NamingEnumeration<SearchResult> answer =dc.search(searchBase, searchFilter, searchCtls);
while (answer.hasMoreElements()) {
SearchResult sr =(SearchResult) answer.next();
setUserModelList(sr,userList);
}
} catch (Exception e) {
e.printStackTrace();
}
return userList;
}
private void setUserModelList(SearchResult sr,List<SysUser> userList){
SysUser user =new SysUser();
NamingEnumeration<?extends Attribute> attrs = sr.getAttributes().getAll();
while (attrs.hasMoreElements()){
Attribute attribute = attrs.nextElement();
String attr_str = attribute.toString();
int spilt_num = attr_str.indexOf(":");
String attr_param = attr_str.substring(0, spilt_num);
String attr_value = attr_str.substring(spilt_num +2);
if (attr_param.equals("name")) {
user.setUserName(attr_value);
} else if (attr_param.equals("displayName")) {
user.setNickName(attr_value);
} else if (attr_param.equals("mail")) {
user.setEmail(attr_value);
} else if (attr_param.equals("mobile")) {
user.setPhonenumber(attr_value);
} else if (attr_param.equals("telephonenumber")) {
user.setTelephoneNumber(attr_value);
} else if (attr_param.equals("initials")) {
user.setSex("男".equals(attr_value)?"0":"1");
}
}
String name = sr.getName();
String[] split = name.split(",");
if (split.length>1) {
String[] split1 = split[1].split("=");
if (split1.length>1) {
user.setAvatar(split1[1]);
}
}
if (!userList.contains(user)) {
userList.add(user);
}
}
/**
* @Description:指定搜索节点搜索指定域用户
* @author moonxy
* @date 2018-05-15
*/
public SearchResultsearchByUserName(String searchBase, String userName) {
SearchControls searchCtls =new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter ="sAMAccountName=" + userName;
String returnedAtts[] ={ "memberOf" }; //定制返回属性
searchCtls.setReturningAttributes(returnedAtts); //设置返回属性集
try {
NamingEnumeration<SearchResult> answer =dc.search(searchBase, searchFilter, searchCtls);
return answer.next();
} catch (Exception e) {
System.err.println("指定搜索节点搜索指定域用户失败");
e.printStackTrace();
}
return null;
}
}
2.AD域用户登录验证
原理:根据用户提交的用户名和密码,去连接AD域认证。通过用户名在AD域指定节点下查找出该用户的nameInNamespace,这个属性是用户最精确的base。通过这个base跟密码去AD验证。
@Component
public class ADUserLoginService{
public BooleanadUserValidate(String username,String password) {
ADUserUtils utils =new ADUserUtils();
SearchResult sr = utils.searchByUserName(utils.root, username);
utils.close();
String nameInNamespace ="";
if (sr!=null) {
nameInNamespace = sr.getNameInNamespace();
}
Boolean flag =true;
DirContext ctx=null;
Properties env =new Properties();
String ldapURL ="ldap://ip:port";//ip:port
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");//LDAP访问安全级别:"none","simple","strong"
env.put(Context.SECURITY_PRINCIPAL, nameInNamespace);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.PROVIDER_URL, ldapURL);
env.put("com.sun.jndi.ldap.connect.timeout", "3000");
try {
ctx =new InitialLdapContext(env,null);// 初始化上下文
System.out.println("身份验证成功!");
} catch (AuthenticationException e) {
System.out.println("身份验证失败!");
flag =false;
e.printStackTrace();
} catch (javax.naming.CommunicationException e) {
System.out.println("AD域连接失败!");
flag =false;
e.printStackTrace();
} catch (Exception e) {
System.out.println("身份验证未知异常!");
flag =false;
e.printStackTrace();
} finally{
if(null!=ctx){
try {
ctx.close();
ctx=null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
return flag;
}
}
3、AD域nginx端口转发代理
由于生产环境不能直接访问ldap://ip:port,在可以访问AD域的服务器上的nginx上配置
在nginx.conf文件下,添加如下配置:
events {
worker_connections 1024;
}
# ================================================================= #
# --------------------------- ldap端口转发 ------------------------ #
# ================================================================= #
# 说明: 源码configure时需要执行如下命令:
# ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-stream
# make & make install
# 方法一: use
stream {
server {
listen 1389;
proxy_pass 10.196.71.162:1389;
proxy_timeout 5s;
proxy_connect_timeout 5s;
proxy_busy_buffers_size 64k;
}
}
# 方法二(负载均衡方案)
stream {
upstream ldap {
hash $remote_addr consistent;
server 10.6.60.122:389;
}
server {
listen 10000;
proxy_connect_timeout 5s;
proxy_timeout 5s;
proxy_pass ldap;
}
}
http {
...
}
生产服务器申请开通访问nginx所在机器的ldap转发端口,可以通过ldap://nginx:转发端口,访问AD域