Ldap 密碼字段被加密,在用戶登錄 或者 用戶更改密碼時候都需要密碼驗證:
1.自己的實現
/** * 集成Ldap登錄驗證 * * @author wzc * @date 2017年12月11日 下午2:14:37 * */ @Service public class LdapLogin { /** * 創建一個LdapTemplate對象 * 連接ldap */ @Autowired private LdapTemplate ldapTemplate; private ContextSource contextSource; /** * 登錄驗證Ldap * @param cn ,登錄的用戶名 * @param pwd 密碼 * @return */ public boolean loginLdap(String cn , String pwd){ //根據cn ,構建DN String dn = getDnForUser(cn); //密碼檢驗 boolean result = authenticate(dn,pwd); return result; } /** * 根據用戶名密碼驗證 * @param userCn 用戶名 * @param pwd 密碼 * @return */ @SuppressWarnings("unchecked") public boolean authenticate(String userCn, String pwd) { DirContext ctx = null; System.out.println(userCn +":"+pwd); try { //調用ldap 的 authenticate方法檢驗 boolean authenticate = ldapTemplate.authenticate(userCn, "(objectclass=person)", pwd); return authenticate; } catch (Exception e) { e.printStackTrace(); return false; } finally { LdapUtils.closeContext(ctx); } } /** * 根據cn 構建出 entry 的 Dn * @param cn * @return */ @SuppressWarnings({ "unused", "unchecked" }) private String getDnForUser(String cn) { List<String> results = ldapTemplate.search("", "(&(objectclass=person)(cn="+cn+"))", new DnMapper()); if (results.size() != 1) { throw new RuntimeException("User not found or not unique"); } System.out.println(results.get(0)); return results.get(0); } } /** * * 節點的 Dn映射 * * @author wzc * @date 2017年12月12日 上午11:21:09 * */ class DnMapper implements ContextMapper{ @Override public String mapFromContext(Object ctx) { DirContextAdapter context = (DirContextAdapter)ctx; Name name = context.getDn(); String dn = name.toString(); return dn; } }
參考 http://angelbill3.iteye.com/blog/2321533
如果要總結Spring的LDAP(Spring開發的操作LDAP的開源Jar),必須要從LDAP說起。
LDAP:Lightweight Directory Access Protocol,翻譯過來是輕量級目錄訪問協議。
它是基於X.500標准的(X.500:構成全球分布式名錄系統的協議),說的這么抽象基本上理解不了,只需要知道是一種協議,以目錄的形式(結構樹)來管理資原(用戶、用戶組、地址簿、郵件用戶等)。一些大公司會選擇以LDAP來存儲用戶及其信息。
所以就像是數據庫一般,LDAP也是有client端和server端。server端是用來存放資源,client端用來操作增刪改查等操作。
1. LDAP:Schema
在LDAP中目錄是按照樹型結構組織——目錄信息樹(DIT)
directory information tree (DIT).
DIT由(Entry)組成,條目相當於關系數據庫中表的記錄;
條目是具有分辨名DN(Distinguished Name)的屬性-值對(Attribute-value)的集合。(DN相當於關系型數據庫表中的主鍵Primary key)
關於LDAP的基礎要學習的還有很多,比如客戶端的安裝、數據模型的學習等等。
2. Spring LDAP
Spring LDAP就是基於JAVA開發的LDAP客戶端開源工具,主要用來操用LDAP,其實現方法有點類似Spring JdbcTemplate(這個大家都非常熟悉了~)
支持Transaction (事務)
支持Pooling (連接池)
官網:http://www.springframework.org/ldap
官方文檔及例子(重要):http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/reference/
JAVA文檔(重要):http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/apidocs/
GitHub(大量例子):https://github.com/spring-projects/spring-ldap
3. 核心類:LdapTemplate
這個類非常類似Spring JdbcTemplate,JdbcTemplate的實現是通過傳入sql語句和RowMapper,query返回目標列表,或是傳入sql和參數,執行update方法。JdbcTemplate的優點是簡化了與數據庫連接的代碼(實現了ldap屬性到對象的映射,使得代碼更為簡單和優雅),以及避免了一些常見的錯誤。(這個開源已經更新到4+版本了,可見其應用之廣)。
優點都是相通的,Spring LdapTemplate的優點是簡化了與LDAP交互的代碼(傳統的類詳見:
http://docs.oracle.com/javase/7/docs/api/javax/naming/directory/package-summary.html)以及避免了一些常見的錯誤。
4. 怎樣理解Autherntication
要驗證一個LDAP的Entry的身份(有點類似於用戶名、密碼登陸),LDAP的思路是通過DN搜索到目標Entry(例如一個公司員工),那么再通過這個Entry和password來驗證合法性。
具體的業務比如是:一個員工要登陸公司網站,輸入他的員工號和密碼。我們是不能通過查詢在LDAP里拿到用戶的密碼(安全性的限制),那么只能傳入實際的密碼,讓LDAP server端驗證合法性。
ldapTemplate.authenticate(LdapQuery query, String password);
在使用這個方法的時候曾經遇到過一個問題,如下:
調用ldapTemplate.authenticate時驗證老是通不過(always return false),經查文檔發現:如果ldap連接是有連接池的話,那么總是調用已創建好的連接去驗證,這樣是錯誤的。Authenticate的驗證過程需要ContextSource通過傳入的待驗證的用戶名和密碼來重新綁定生成一個連接,也就是說這個方法要使用到的connection連接並不能是連接池里的那個connection。
所以需要new一個LdapContextSource類和LdapTemplate類,再通過LdapTemplate類的setContextSource(ContextSource contextSource)將持有用戶名密碼的ContextSource傳入。
注意:在contextSource創建后,需要調用afterPropertiesSet()方法來驗證所有必要的參數都已經set了(特指url、用戶名、密碼等),這個方法執行后,真正的contextSource才會被實例化。(特別是在Spring context上下文之外的配置中,必須要執行該方法。
這么說好像很抽象,具體代碼如下:
1.LdapContextSource contextSource = new LdapContextSource(); 2.contextSource.setUrl(url); 3.contextSource.setUserDn(userDn); 4.contextSource.setPassword(userPwd); 5.contextSource.setPooled(false); 6.contextSource.afterPropertiesSet(); // important 7. 8.LdapTemplate template = new LdapTemplate(); 9.template.setContextSource(contextSource); 10. 11.Boolean result = template.authenticate(LDAP_BASE_DN, filter, pwd);
5. Pooling
Spring LDAP的pool用的是apache commons pool(http://commons.apache.org/proper/commons-pool/index.html)
6. 通過SSL的認證方式連接
這塊公司是用IBM的portal來安裝的SSL,所以對於tomcat的配置並不是很了解。可以在stack-overflow上看看相關資料。
【總結】
在使用Spring ldap的一年多時間里,沒有碰到太過復雜的問題,產品上線后表現也很穩定。總得來說因為跟大家都熟悉的JdbcTemplate思想上有些相似,所以學習起來成本也不高。