目录
AD域简单介绍
AD域的存储结构跟我们所用的文件夹一样,所以要定位某个文件就需要它的文件路径。然后对它做增删改查。
组织:
OU=100.27.04,OU=100.27,OU=100,OU=wall,DC=gwtest,DC=com,DC=cn
人员:
CN=100000,OU=100.27.04,OU=100.27,OU=100,OU=wall,DC=test,DC=com,DC=cn
正常系统是区分组织和部门的,域中就不需要这个概念了,部门就变成一层一层的了,这个OU就相当于一层一层的文件路径,CN就是特定的文件,设置不同类型就是不同文件 就像 word excel等。
OU=wall,DC=gwtest,DC=com,DC=cn 就相当于指定服务器的信息路径。
需求解析
组织/部门
新增/部门
组织/部门新增就相当于在域中创建对应的OU,流程如下
修改
这里涉及一个点就是,很多组织都有相同的部门,比如财务部,人力资源部等等。单独拿出某个部门就不清楚是那个组织下的,定义了新的需求所有部门岗位的名称都要 “组织_多级部门_多级岗位”这样展示。
停用
需求上没有要组织停用的功能,只要求了部门停用。停用对应域内删除,很简单就不画流程图了,就是找到对应部门OU 删除即可。
组织/部门调动
这个调动因为产品设计时间轴的概念不是和好实现,所以定时就抓取不到调动前的数据,所以商量设计成为实时同步。流程也很简单。
岗位
岗位在域那边对应通讯组方便后续OA对接,岗位的地址也跟人员一样有CN只不过类型不一样,CN=100000,OU=100.27.04,OU=100.27,OU=100,OU=wall,DC=test,DC=com,DC=cn,刚为放在部门下与人员同级。
新增
新增流程同组织新增
修改
修改流程同组织修改
调动
岗位调动影响人员信息在人员信息中处理
人员
新增
人员新增流程如下
调动
人员调动流程如下
修改
停用
开发解析
连接域的工具类
连接域
非证书方式
连接代码
Properties mEnv = new Properties();
mEnv.put(Context.AUTHORITATIVE, "true");
mEnv.put(Context.SECURITY_PROTOCOL, "ssl");
mEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
mEnv.put(Context.PROVIDER_URL,Url);
mEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
mEnv.put(Context.SECURITY_PRINCIPAL, user);
mEnv.put(Context.SECURITY_CREDENTIALS, password);
//关键代码,注意对应的DummySSLSocketFactory这个类的包路径要正确,
mEnv.put("java.naming.ldap.factory.socket", "nc.ctjt.itf.arap.DummySSLSocketFactory");
dc = new InitialLdapContext(mEnv, null);
证书方式
首先要导入证书,要导入到jdk中命令如下
keytool -import -keystore /media/data/java-1.8.0-openjdk/jre/lib/security/cacerts -storepass changeit -keypass changeit -alias ca4 -file /media/data/demo/ca4.cer -trustcacerts
导入位置 /media/data/java-1.8.0-openjdk/jre/lib/security/cacerts
别名 ca4
文件所在位置 /media/data/demo/ca4.cer
连接代码
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, user);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.PROVIDER_URL,Url);
env.put(Context.AUTHORITATIVE, "true");
System.setProperty("javax.net.ssl.trustStore",keystore);
env.put(Context.SECURITY_PROTOCOL, "ssl");
dc = new InitialLdapContext(env,null);
关闭域
public void close() throws BusinessException {
if (dc != null) {
try {
dc.close();
} catch (NamingException e) {
throw new BusinessException("AD域关闭异常,"+e.getMessage());
}
}
}
查询用户属性
searchBase 用户的OU
userName 用户属性 一般都是编码
public boolean searchByUserName(String searchBase, String userName) throws BusinessException {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "cn=" + userName;
String returnedAtts[] = { "cn" }; // 定制返回属性
searchCtls.setReturningAttributes(returnedAtts); // 设置返回属性集
try {
NamingEnumeration<SearchResult> answer = dc.search(searchBase, searchFilter, searchCtls);
if(answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attribute displayName = sr.getAttributes().get("cn");
if(userName.equals(displayName.get())) {
return false;
}
}
} catch (Exception e) {
return false;
}
return true;
}
查询岗位组
@Override
public boolean searchByUserGroup(String searchBase, String userName) throws BusinessException {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "member" + userName;
String returnedAtts[] = {"member"}; // 定制返回属性
searchCtls.setReturningAttributes(returnedAtts); // 设置返回属性集
try {
NamingEnumeration<SearchResult> answer = dc.search(searchBase, searchFilter, searchCtls);
if(answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attribute displayName = sr.getAttributes().get("member");
if(userName.equals(displayName.get())) {
return false;
}
}
} catch (Exception e) {
return true;
}
return true;
}
查询组织信息
@Override
public boolean searchByOrg(String searchBase,String name ,String description,String displayName) throws BusinessException {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "name=" + name;
String returnedAtts[] = { "description","displayName","name"}; //定制返回属性
searchCtls.setReturningAttributes(returnedAtts); // 设置返回属性集
try {
NamingEnumeration<SearchResult> answer = dc.search(searchBase, searchFilter, searchCtls);
if(answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attribute AD_description = sr.getAttributes().get("description");
Attribute AD_displayName = sr.getAttributes().get("displayname");
if(StringUtil.isNotBlank(description)&&StringUtil.isNotBlank(displayName))
//两边空
if(displayName == null && AD_displayName.get() == null) {
if(description.equals(AD_description.get())) {
return true;
}
}else {
if(displayName.equals(AD_displayName.get()) && description.equals(AD_description.get())) {
return true;
}
}
}
} catch (Exception e) {
return false;
}
return false;
}
修改密码
@Override
public void updatePwd(String base, String pwd) {
try {
ModificationItem[] mods = new ModificationItem[1];
String password = "\"" + pwd + "\"";
byte[] newUnicodePassword = password.getBytes(StandardCharsets.UTF_16LE);
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute("unicodePwd", newUnicodePassword));
dc.modifyAttributes(base, mods);
System.out.println("修改密码成功!");
} catch (Exception e) {
e.printStackTrace();
}
}
新增人员
@Override
public boolean add(HRADVo psndoc, String base) throws BusinessException {
try {
Attributes attrs = new BasicAttributes(true);
if(StringUtil.isNotBlank(psndoc.getEmail())) {
attrs.put("mail", psndoc.getEmail());
attrs.put("UserPrincipalName", psndoc.getEmail());
}
if(StringUtil.isNotBlank(psndoc.getCode())) {
attrs.put("EmployeeNumber", psndoc.getCode());
attrs.put("cn", psndoc.getCode());// 新增用户名称
attrs.put("SamAccountName", psndoc.getCode());
}
if(StringUtil.isNotBlank(psndoc.getMobile())) {
attrs.put("mobile", psndoc.getMobile());
}
if(StringUtil.isNotBlank(psndoc.getName())) {
attrs.put("displayName", psndoc.getName());//
}
if(StringUtil.isNotBlank(psndoc.getOfficephone())) {
attrs.put("telephoneNumber", psndoc.getOfficephone());//
}
attrs.put("objectclass", "user");
attrs.put("userAccountControl", "****");
// 设置为0.用户首次登录需要改密码
attrs.put("pwdLastSet", "0");
// 设置默认密码
String password = "\"" + "******" + "\"";
byte[] newUnicodePassword = password.getBytes(StandardCharsets.UTF_16LE);
attrs.put("unicodePwd", newUnicodePassword);// 默认密码
dc.createSubcontext(base, attrs);
Logger.error("新增AD域用户成功:" + psndoc.getCode());
} catch (Exception e) {
return false;
}
return true;
}
调动用户
public boolean turnUser(String oldName, String newName) throws BusinessException {
try {
dc.rename(oldName, newName);
} catch (NamingException e) {
return false;
}
return true;
}
修改用户属性
public boolean modifyUser(HRADVo psndoc, String base) throws BusinessException {
//设置属性
//修改的属性
List<ModificationItem> mList = new ArrayList<ModificationItem>();
if(StringUtil.isNotBlank(psndoc.getEmail())) {
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("mail", psndoc.getEmail())));
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("UserPrincipalName", psndoc.getEmail())));
}
if(StringUtil.isNotBlank(psndoc.getCode())) {
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("EmployeeNumber", psndoc.getCode())));
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("SamAccountName", psndoc.getCode())));
}
if(StringUtil.isNotBlank(psndoc.getMobile())) {
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("mobile", psndoc.getMobile())));
}
if(StringUtil.isNotBlank(psndoc.getName())) {
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("displayName", psndoc.getName())));
}
if(StringUtil.isNotBlank(psndoc.getOfficephone())) {
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("telephoneNumber", psndoc.getOfficephone())));
}
//集合转为数组
ModificationItem[] mArray = new ModificationItem[mList.size()];
for (int i = 0; i < mList.size(); i++) {
mArray[i] = mList.get(i);
}
try {
dc.modifyAttributes(base, mArray);
} catch (NamingException e) {
return false;
}
return true;
}
修改组织信息
public boolean modifyOrg(HRADVo HRADVo, String base) throws BusinessException {
//设置属性
//修改的属性
List<ModificationItem> mList = new ArrayList<ModificationItem>();
//不能修改
//mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("userAccountControl", person.getUserAccountControl())));
// mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("EmailAddress", psndoc.getEmail())));
if(StringUtil.isNotBlank(HRADVo.getShortname())) {
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("displayName", HRADVo.getName())));
}
//集合转为数组
ModificationItem[] mArray = new ModificationItem[mList.size()];
for (int i = 0; i < mList.size(); i++) {
mArray[i] = mList.get(i);
}
try {
dc.modifyAttributes(base, mArray);
} catch (NamingException e) {
return false;
}
return true;
}
修改岗位信息
public boolean modifyPost(String description, String base) throws BusinessException {
//设置属性
//修改的属性
List<ModificationItem> mList = new ArrayList<ModificationItem>();
//不能修改
if(StringUtil.isNotBlank(description)) {
mList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("description", description)));
}
//集合转为数组
ModificationItem[] mArray = new ModificationItem[mList.size()];
for (int i = 0; i < mList.size(); i++) {
mArray[i] = mList.get(i);
}
try {
dc.modifyAttributes(base, mArray);
} catch (NamingException e) {
return false;
}
return true;
}
查询并创建组织
public boolean getOU(String ldapGroupDN,HashMap map) throws BusinessException{
try {
dc.getAttributes(ldapGroupDN);
return true;
} catch (NamingException e) {
/*
* 没有找到对应的Group条目,新增Group条目
*/
boolean flag = true;
//3.2 找到域存在的节点
String[] urllist = ldapGroupDN.split(",");
int i = 0;
while (flag) {
flag = false;
//域 中 都没有 将数组下标越界结束循环
int index = ldapGroupDN.indexOf(urllist[i]+",");
String newUrl = ldapGroupDN.substring(index+(urllist[i]+",").length(), ldapGroupDN.length());
i++;
try {
Attributes attrs = dc.getAttributes(newUrl);
} catch (Exception e2) {
flag = true;
}
}
//3.2.1 按照顺序以此增加 OU
for(int j=i-1;j>=0;j--) {
int index = ldapGroupDN.indexOf(urllist[j]+",");
String newldapGroupDN = ldapGroupDN.substring(index, ldapGroupDN.length());
String ou = urllist[j].substring(3);
Attributes attrs = new BasicAttributes();
HRADVo HRADVo = (HRADVo) map.get("OU="+ou);
//创建objectclass属性
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("organizationalunit");
//创建cn属性
Attribute cn = new BasicAttribute("ou", ou);
Attribute name = new BasicAttribute("name", ou);
attrs.put(name);
attrs.put(objclass);
attrs.put(cn);
if(null != HRADVo.getName()) {
//处理部门
if("vno".equals(HRADVo.getVno())) {
Attribute description = new BasicAttribute("description", HRADVo.getDef4()+"_" + HRADVo.getDef1().substring(0, HRADVo.getDef1().length()-1));
attrs.put(description);
}else {
Attribute description = new BasicAttribute("description", HRADVo.getName());
attrs.put(description);
}
}
if(StringUtil.isNotBlank(HRADVo.getShortname())) {
Attribute description = new BasicAttribute("displayName", HRADVo.getShortname());
attrs.put(description);
}
//将属性绑定到新的条目上,创建该条目
try {
dc.bind(newldapGroupDN, null, attrs);
} catch (NamingException e1) {
return false;
}
}
}
return true;
}
查询组织
public boolean getGroup(String ldapGroupDN) throws BusinessException{
try {
dc.getAttributes(ldapGroupDN);
return true;
} catch (NamingException e) {
return false;
}
}
修改组织
public void updateOU(String oldName,String newName) throws Exception{
try {
dc.rename(oldName, newName);
Logger.info("修改成功!");
} catch (Exception e) {
Logger.info("修改失败!");
}
}
删除人员
@Override
public boolean deleteUser(String userDN) throws BusinessException {
try {
dc.destroySubcontext(userDN);
} catch (Exception e) {
return false;
}
return true;
}
删除组织
@Override
public boolean deleteOU(String base) throws Exception {
try {
dc.removeFromEnvironment(base);
}catch (Exception e) {
return false;
}
return true;
}
人员添加到岗位组
public boolean addUserToGroup(String username, String groupName)
{
ModificationItem[] mods = new ModificationItem[1];
Attribute member = new BasicAttribute("member",username);
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, member);
try {
dc.modifyAttributes(groupName, mods);
} catch (NamingException e) {
return false;
}
return true;
}
人员从岗位组删除
public boolean deleteUserFromGroup(String username, String groupName)
{
ModificationItem[] mods = new ModificationItem[1];
Attribute attribute = new BasicAttribute("member",username);
mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attribute);
try {
dc.modifyAttributes(groupName, mods);
} catch (NamingException e) {
return false;
}
return true;
}
新增岗位组
public boolean AddGroup(String post, String postDN,String postname)
{
Attributes attributes=new BasicAttributes();
Attribute attribute=new BasicAttribute("objectClass");
Attribute cn = new BasicAttribute("cn", post);
Attribute sAMAccountName = new BasicAttribute("sAMAccountName", post);
Attribute displayName = new BasicAttribute("displayName", post);
Attribute description = new BasicAttribute("description", postname);
attribute.add("group");
attributes.put(attribute);
attributes.put("groupType","2");
attributes.put(cn);
attributes.put(sAMAccountName);
attributes.put(displayName);
attributes.put(description);
try {
dc.createSubcontext(postDN,attributes);
} catch (NamingException e) {
return false;
}
return true;
}
获取域OU结构
由前面介绍域的架构就发现域的存储结构跟我们数据库不一样,mysql oracle dm 等都是以表的形式存储,设置父字段存储上级主键
例:组织表 org
pk_org | pk_fatherorg |
---|---|
1 | |
2 | 2 |
3 | 3 |
我们这个就要根据操作对象的主键查询
SELECT
code ,
name
FROM
org START
WITH pk_org = ? CONNECT BY PRIOR pk_fatherorg = pk_org
具体代码如下
public HashMap getORGURL(String pk_org,String pk_dept) throws Exception {
HashMap map = new HashMap();
String url = "";
StringBuffer post = new StringBuffer();
try {
if(StringUtil.isNotBlank(pk_org)) {
ArrayList<HRADVo> orgList = getAdSqlUtil().getOrgCode(pk_org);
for(int i =orgList.size()-1;i>=0;i--) {
if(i==orgList.size()-1) {
if(StringUtil.isNotBlank(orgList.get(i).getDef3())) {
url = "OU="+orgList.get(i).getDef3()+",";
map.put("OU="+orgList.get(i).getDef3(), orgList.get(i));
}else {
url = "OU="+orgList.get(i).getCode()+",";
map.put("OU="+orgList.get(i).getCode(), orgList.get(i));
}
}else {
if(StringUtil.isNotBlank(orgList.get(i).getDef3())) {
String[] ul = url.split(",");
url = ul[0]+"."+orgList.get(i).getDef3()+","+url;
map.put(ul[0]+"."+orgList.get(i).getDef3(), orgList.get(i));
}else {
String[] ul = url.split(",");
url = ul[0]+"."+orgList.get(i).getCode()+","+url;
map.put(ul[0]+"."+orgList.get(i).getCode(), orgList.get(i));
}
}
}
}
if(StringUtil.isNotBlank(pk_dept)) {
ArrayList<HRADVo> codeList = getAdSqlUtil().getDeptCode(pk_dept);
for(int i =codeList.size()-1;i>=0;i--) {
if(StringUtil.isNotBlank(codeList.get(i).getShortname())) {
post.append(codeList.get(i).getShortname()+"_");
codeList.get(i).setDef1(post.toString());
}else if(StringUtil.isNotBlank(codeList.get(i).getName())){
post.append(codeList.get(i).getName()+"_");
codeList.get(i).setDef1(post.toString());
}
if(StringUtil.isNotBlank(codeList.get(i).getDef3())) {
String[] ul = url.split(",");
url = ul[0]+"."+codeList.get(i).getDef3()+","+url;
map.put(ul[0]+"."+codeList.get(i).getDef3(), codeList.get(i));
}else {
String[] ul = url.split(",");
url = ul[0]+"."+codeList.get(i).getCode()+","+url;
map.put(ul[0]+"."+codeList.get(i).getCode(), codeList.get(i));
}
}
}
map.put("url", url);
map.put("post", post);
} catch (Exception e) {
throw new Exception(e);
}
return map;
}
根据这种方式就可以获得OU=100.27.04,OU=100.27,OU=100,这样的结构,在读取配置文件中域的地址(OU=wall,DC=gwtest,DC=com,DC=cn)就可以获取对应域中的OU地址