Spring Ldap user登錄 userPassword驗證


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思想上有些相似,所以學習起來成本也不高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM