如何使用JNI获取Active Directory用户属性?新手入门咨询
嘿,刚好我对这块有点了解,来给你梳理清楚!
简单来说,Active Directory(简称AD)是微软推出的目录服务,企业里几乎都会用它来集中管理用户账号、计算机、权限组这些资源。比如你公司的办公电脑登录账号、邮件账号,大概率都是存在AD里的,管理员可以通过AD统一设置谁能访问哪些系统、文件。
JNI是Java调用本地C/C代码的桥梁,所以核心思路就是:Java层声明native方法,然后用C/C调用Windows原生的AD接口来查询用户属性,最后把结果返回给Java。下面是具体步骤:
1. 先熟悉Windows的AD本地API
Windows提供了两种主要的本地接口来操作AD:
- ADSI(Active Directory Service Interfaces):基于COM的接口,封装了LDAP的复杂操作,用起来更简单
- LDAP API:更底层的LDAP协议接口,灵活性更高
推荐先用ADSI上手,门槛低一些。
2. 编写Java层的Native方法
先写一个Java类,声明要调用的native方法,同时加载后续编译好的动态链接库:
public class ADUserReader { // 静态代码块加载本地库,"ADUserNative"是你编译出的dll文件名(不带.dll后缀) static { System.loadLibrary("ADUserNative"); } // Native方法:传入用户名,返回AD用户的属性字符串(你也可以改成返回Map等结构) public native String getUserADAttributes(String username); }
3. 生成JNI头文件
用Java的工具生成对应JNI的头文件,打开命令行,先编译Java类:
javac ADUserReader.java
然后用javah生成头文件:
javah -jni ADUserReader
执行后会得到一个ADUserReader.h的头文件,里面是JNI函数的标准声明,接下来的C/C++代码要实现这个函数。
4. 编写C/C++实现代码
这里用ADSI来实现,需要链接Windows的ADSI相关库。代码示例如下:
#include "ADUserReader.h" #include <windows.h> #include <adsi.h> #include <atlbase.h> #include <string> #include <comutil.h> // 链接ADSI所需的库 #pragma comment(lib, "adsiid.lib") #pragma comment(lib, "activeds.lib") #pragma comment(lib, "comsuppw.lib") JNIEXPORT jstring JNICALL Java_ADUserReader_getUserADAttributes(JNIEnv *env, jobject obj, jstring jUsername) { // 把Java字符串转成C字符串 const char* username = env->GetStringUTFChars(jUsername, NULL); if (username == NULL) { return NULL; } HRESULT hr; CComPtr<IADs> pADsUser; std::string resultStr; // 构造AD用户的LDAP路径,需要替换成你的实际AD域名和用户组织单元 std::string ldapPath = "LDAP://CN=" + std::string(username) + ",OU=Users,DC=yourcompany,DC=com"; // 绑定到AD用户对象:如果需要身份验证,可以在第2、3参数传入有权限的账号和密码 hr = ADsOpenObject( ldapPath.c_str(), NULL, // 用户名,NULL表示用当前登录用户身份 NULL, // 密码 ADS_SECURE_AUTHENTICATION, // 安全认证方式 IID_IADs, (void**)&pADsUser ); if (SUCCEEDED(hr)) { // 获取用户属性,这里举几个常用的例子,你可以根据需求添加更多 VARIANT attrValue; VariantInit(&attrValue); // 获取用户全名(displayName) hr = pADsUser->Get(CComBSTR("displayName"), &attrValue); if (SUCCEEDED(hr) && attrValue.vt != VT_EMPTY) { resultStr += "全名: " + std::string(_bstr_t(attrValue.bstrVal)) + "\n"; VariantClear(&attrValue); } // 获取邮箱(mail) hr = pADsUser->Get(CComBSTR("mail"), &attrValue); if (SUCCEEDED(hr) && attrValue.vt != VT_EMPTY) { resultStr += "邮箱: " + std::string(_bstr_t(attrValue.bstrVal)) + "\n"; VariantClear(&attrValue); } // 获取用户登录名(userPrincipalName) hr = pADsUser->Get(CComBSTR("userPrincipalName"), &attrValue); if (SUCCEEDED(hr) && attrValue.vt != VT_EMPTY) { resultStr += "登录名: " + std::string(_bstr_t(attrValue.bstrVal)) + "\n"; VariantClear(&attrValue); } } else { // 绑定失败,返回错误信息 resultStr = "绑定AD用户失败: " + std::string(_com_error(hr).ErrorMessage()); } // 释放Java字符串资源 env->ReleaseStringUTFChars(jUsername, username); // 把C字符串转成Java字符串返回 return env->NewStringUTF(resultStr.c_str()); }
注意:LDAP://CN=...这个路径要根据你的实际AD环境修改,比如你的用户在OU=研发部,DC=company,DC=cn下,就要调整对应的部分。
5. 编译C/C++代码为动态链接库
用Visual Studio的话,创建一个Win32动态链接库项目,把上面的代码添加进去,确保项目设置里链接了需要的库(adsiid.lib、activeds.lib、comsuppw.lib),编译后会生成ADUserNative.dll。
6. 运行Java测试程序
把生成的dll放在Java程序能找到的位置(比如和class文件同目录,或者加入系统PATH),然后写个测试类:
public class ADTest { public static void main(String[] args) { ADUserReader reader = new ADUserReader(); // 替换成你AD里存在的用户名 String userAttrs = reader.getUserADAttributes("zhangsan"); System.out.println(userAttrs); } }
运行这个类,就能看到AD用户的属性啦!
- 环境要求:运行程序的机器必须能访问AD服务器,最好是加入了AD域的机器
- 权限问题:如果当前登录用户没有访问AD的权限,要在
ADsOpenObject里传入有查询权限的AD账号和密码 - 错误处理:JNI代码里要做好异常捕获,避免Java程序崩溃
- 替代方案:其实Java本身有JNDI(Java Naming and Directory Interface)可以直接操作AD,不需要JNI,如果你不是必须用JNI的话,用JNDI会更简单哦!
内容的提问来源于stack exchange,提问作者Kasinaat 007

