tomcat容器中使用JNDI通路外部openLDAP提供的目錄服務
- tomcat容器中使用JNDI通路外部openLDAP提供的目錄服務
- Preface
- 環境工具
- 主要參考文檔
- JNDI調用外部服務基本過程說明
- 對應修改WebServicejava 為了友善使用ldap的通路直接放在了該類中看起來有些不舒服實際應用中可以單獨分開
- OpenLDAP 配置過程及檔案
Preface
- 本smaple 使用OpenLDAP 提供目錄服務,并使用Berkeley DB 作為backend
- 在之前jerseyRestfulservice&Client的基礎上需要進行細微的修改
- 參考tutorialspoint
- 本文原創,允許轉載但務必貼出本文連結
環境&工具
- Jersey JAX-RS 2.0 RI bundle 解壓後将所有.jar檔案放在WebContent>WEB-INF>lib下面
- json.jar 注:因為eclipse有特殊的喜好,在web servicejar 工程中,jar包的引用根據web容器而有所不同,對于tomcat,所有引用.jar必須放在web-info/lib/下
- OpenLDAP version “openldap-2.4.40”
- Berkeley DB version “db-4.7.25”
- bdb-dev(4.8)Ubuntu 下可直接使用”sudo apt-get install libdb-dev”(注:bdb的文檔中沒看到對該包的依賴)
- java version “1.8.0_40”
- tomcat 8.0
- eclipse Luna
- mysql 5.6.19
主要參考文檔
- OpenLDAP Software 2.4 Administrator’s Guide | 中文版相關概念
- Configure LDAP service in tomcat
- stackoverflow
- stackexchange
- Q&A emails that could be found in OpenLDAP’s host
JNDI調用外部服務基本過程說明
- JNDI wikipedia
- OpenLdap wikipedia
- 基本概念:
- JNDI: Java Naming and Directory Interface
- 可以由多種方式實作JNDI服務
- 在server/context.xml 中添加resource,容器會自動添加該外部服務,然後在servlet中lookup()
- 在serverlet 中手動添加通路配置,通過context factory生成該服務的context,然後在servlet中lookup()
- 在ejb中注入(injection resource)
- ldap及openLDAP:概念頗多,需要參考OpenLDAP Software 2.4 Administrator’s Guide | 中文版相關概念
對應修改WebService.java (為了友善使用,ldap的通路直接放在了該類中,看起來有些不舒服,實際應用中可以單獨分開)
package webService;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Hashtable;
import java.util.List;
/* receive GET & POST requests from http://localhost:8080/RestfulService/API/restService */
@Path("/restService")
public class WebService {
/* receive json data & search in mysql with it */
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response json_restResponse(InputStream incomingData) {
// json receiving variables
String receivedString = "";
JSONObject receivedJson = null;
// temp variables
// returnCode will be send to client and be present in the console view
String returnCode = "SEARCH PROGRESS & RESULTS:";
// receive the json data as receivedJson(JSONObject)
try {
BufferedReader in = new BufferedReader(new InputStreamReader(incomingData));
String line = null;
while ((line = in.readLine()) != null) {
receivedString += line;
}
try {
// convert data stream to json
receivedJson = new JSONObject(receivedString);
returnCode += "\n\n- receive json data successfully...";
// call jpa method
List<String> title = JpaHandle.getTitle();
System.out.println(receivedJson.toString());
for(String e:title) {
returnCode += ("\n\ntitle:\t" + e);
if (e == receivedJson.getString("title")) {
returnCode += "\t-that's it!";
} else {
returnCode += ("\t-not the one I'm finding..." + e);
}
}
} catch (JSONException e){
System.out.println(e);
}
} catch (Exception e) {
System.out.println(e);
}
return Response.status().entity(returnCode).build();
}
/* receive the GET requests & test whether the server is on */
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
String returnCode = "this is a test~";
try {
ldapSearch();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("ldapSearch error");
}
return returnCode;
}
// Search for the full name of a person whoes uid equals to "fbloggs"
// (or we can search for the password for identity recognization)
public static String ldapSearch() throws Exception {
DirContext ldapContext = null;
ldapContext = createLDAPContext();
String returnCode = "";
// Set the filter
String uid = "fbloggs";
String filter = "(&(objectClass=inetOrgPerson)(uid=" + uid + "))";
try {
// Set the contents that will be shown
String[] attrPersonArray = { "uid", "userPassword", "cn", "sn", "mail" };
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchControls.setReturningAttributes(attrPersonArray);
// Receive result & print out
// NamingEnumeration answer = ldapContext.search("dc=sample,dc=com", filter.toString(), searchControls);
NamingEnumeration answer = ldapContext.search("", filter.toString(), searchControls);
StringBuffer sb = new StringBuffer();
while (answer.hasMore()) {
SearchResult result = (SearchResult) answer.next();
Attributes attrs = result.getAttributes();
if (answer.hasMore()){
sb.append(attrs.get("cn").toString().substring() + ",");
}
else{
sb.append(attrs.get("cn").toString().substring());
}
}
returnCode += sb.toString();
System.out.println("before-");
System.out.println(returnCode);
System.out.println("-after");
}catch(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("search & output error");
}
// Close the context when we're done
closeLDAPContext(ldapContext);
return returnCode;
}
// Create ldapContext
public static DirContext createLDAPContext() {
// Set up environment for creating initial context
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/dc=sample,dc=com");
// Authenticate as S. User and password "mysecret"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Manager,dc=sample,dc=com");
env.put(Context.SECURITY_CREDENTIALS, "secret");
DirContext ldapContext = null;
try {
// Create initial context
ldapContext = new InitialDirContext(env);
System.out.println(ldapContext.toString());
System.out.println("create ldap context successfully");
} catch (NamingException e) {
e.printStackTrace();
}
return ldapContext;
}
// Close ldapContext
public static void closeLDAPContext(DirContext ldapContext) {
try {
ldapContext.close();
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
OpenLDAP 配置過程及檔案
- 安裝Berkeley DB
- 現在對bdb的支援不算很好,可以選擇其他方案
- 添加bdb-dev包
- cd build_unix
- ../dist/configure
- make
- make install
- 環境變量的設定:
- env CPPFLAGS=”-I/usr/local/BerkeleyDB.4.7/include” LDFLAGS=”-L/usr/local/BerkeleyDB.4.7/lib”
- 安裝OpenLDAP
- 文檔中有openLDAP的安裝步驟,不過看網上讨論以及親自實踐,還是要準備好不斷調試的心情。
- 以下是從各個文章中搜集到的測試手段(或者也可直接翻api):
- 檢視bdb中的現有目錄:ldapsearch -x -b ” -s base ‘(objectclass=*)’ namingContexts
- 嘗試添加目錄(測試manager登入):ldapadd -x -D ‘cn=Manager,dc=localhost,dc=com’ -W
- 測試配置檔案正确性:sudo slaptest
- 以debug模式啟動slapd:sudo slapd -d 256(通過JNDI使用slapd的時候也可以從這裡看到輸出)
- 其他的一些tips:
- ./configure –prefix=/usr/local/openldap –enable-bdb=yes 配置時可以随便選prefix路徑,但最好還是加上enable-bdb(雖然預設會enable該backend,但最好還是顯式配置)
- prefix路徑并不是配置的路徑,配置檔案會在/etc/ldap/下,并且install之後需要rm -rf /etc/ldap/slapd.d/*來删除ldap給的example,不然之後不會被新配置覆寫的
- 配置檔案slapd.conf中的配置項前不可有空格,配置項和值之間最好使用tab
- slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d 将自己的配置檔案slapds.conf生成對應格式的真正配置檔案
- 因為很多都是手動修改生成的檔案,是以很多檔案的所有人和所有組都不是openldap,相關檔案通路權限需要修改
- 配置檔案及一個從bdb讀出的sample