2008-12-23 31 views
63

Tôi có một nhiệm vụ đơn giản để xác thực đối với Active Directory bằng cách sử dụng Java. Chỉ cần xác minh thông tin đăng nhập và không có gì khác. Giả sử tên miền của tôi là "fun.xyz.tld", đường dẫn OU không xác định và tên người dùng/mật khẩu là testu/testp.Xác thực đối với Active Directory bằng Java trên Linux

Tôi biết có một vài thư viện Java ở đó đơn giản hóa tác vụ này, nhưng tôi đã không thành công khi triển khai chúng. Hầu hết các ví dụ mà tôi đã tìm thấy giải quyết LDAP nói chung, không cụ thể Active Directory. Phát hành yêu cầu LDAP có nghĩa là gửi một đường dẫn OU trong đó, mà tôi không có. Ngoài ra, ứng dụng phát hành yêu cầu LDAP nên đã bị ràng buộc vào Active Directory để truy cập vào nó ... Không an toàn, vì thông tin đăng nhập sẽ phải được lưu trữ ở nơi nào đó có thể phát hiện được. Tôi muốn thử nghiệm ràng buộc với thông tin kiểm tra, nếu có thể - điều này có nghĩa là tài khoản hợp lệ.

Cuối cùng, nếu có thể, có cách nào để làm cho cơ chế xác thực như vậy được mã hóa không? Tôi biết rằng AD sử dụng Kerberos, nhưng không chắc liệu các phương thức LDAP của Java có thực hiện hay không.

Có ai có ví dụ về mã hoạt động không? Cảm ơn.

Trả lời

46

Đây là mã tôi đặt cùng nhau dựa trên ví dụ từ blog này: LINK và nguồn này: LINK.

import com.sun.jndi.ldap.LdapCtxFactory; 
import java.util.ArrayList; 
import java.util.Hashtable; 
import java.util.List; 
import java.util.Iterator; 
import javax.naming.Context; 
import javax.naming.AuthenticationException; 
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.SearchControls; 
import javax.naming.directory.SearchResult; 
import static javax.naming.directory.SearchControls.SUBTREE_SCOPE; 

class App2 { 

    public static void main(String[] args) { 

     if (args.length != 4 && args.length != 2) { 
      System.out.println("Purpose: authenticate user against Active Directory and list group membership."); 
      System.out.println("Usage: App2 <username> <password> <domain> <server>"); 
      System.out.println("Short usage: App2 <username> <password>"); 
      System.out.println("(short usage assumes 'xyz.tld' as domain and 'abc' as server)"); 
      System.exit(1); 
     } 

     String domainName; 
     String serverName; 

     if (args.length == 4) { 
      domainName = args[2]; 
      serverName = args[3]; 
     } else { 
      domainName = "xyz.tld"; 
      serverName = "abc"; 
     } 

     String username = args[0]; 
     String password = args[1]; 

     System.out 
       .println("Authenticating " + username + "@" + domainName + " through " + serverName + "." + domainName); 

     // bind by using the specified username/password 
     Hashtable props = new Hashtable(); 
     String principalName = username + "@" + domainName; 
     props.put(Context.SECURITY_PRINCIPAL, principalName); 
     props.put(Context.SECURITY_CREDENTIALS, password); 
     DirContext context; 

     try { 
      context = LdapCtxFactory.getLdapCtxInstance("ldap://" + serverName + "." + domainName + '/', props); 
      System.out.println("Authentication succeeded!"); 

      // locate this user's record 
      SearchControls controls = new SearchControls(); 
      controls.setSearchScope(SUBTREE_SCOPE); 
      NamingEnumeration<SearchResult> renum = context.search(toDC(domainName), 
        "(& (userPrincipalName=" + principalName + ")(objectClass=user))", controls); 
      if (!renum.hasMore()) { 
       System.out.println("Cannot locate user information for " + username); 
       System.exit(1); 
      } 
      SearchResult result = renum.next(); 

      List<String> groups = new ArrayList<String>(); 
      Attribute memberOf = result.getAttributes().get("memberOf"); 
      if (memberOf != null) {// null if this user belongs to no group at all 
       for (int i = 0; i < memberOf.size(); i++) { 
        Attributes atts = context.getAttributes(memberOf.get(i).toString(), new String[] { "CN" }); 
        Attribute att = atts.get("CN"); 
        groups.add(att.get().toString()); 
       } 
      } 

      context.close(); 

      System.out.println(); 
      System.out.println("User belongs to: "); 
      Iterator ig = groups.iterator(); 
      while (ig.hasNext()) { 
       System.out.println(" " + ig.next()); 
      } 

     } catch (AuthenticationException a) { 
      System.out.println("Authentication failed: " + a); 
      System.exit(1); 
     } catch (NamingException e) { 
      System.out.println("Failed to bind to LDAP/get account information: " + e); 
      System.exit(1); 
     } 
    } 

    private static String toDC(String domainName) { 
     StringBuilder buf = new StringBuilder(); 
     for (String token : domainName.split("\\.")) { 
      if (token.length() == 0) 
       continue; // defensive check 
      if (buf.length() > 0) 
       buf.append(","); 
      buf.append("DC=").append(token); 
     } 
     return buf.toString(); 
    } 

} 
+3

'import com.sun.jndi.ldap.LdapCtxFactory;' - điều này rất có thể sẽ chỉ hoạt động với Sun JVM. Cập nhật –

3

Bạn chỉ đang xác minh thông tin xác thực? Trong trường hợp đó bạn chỉ có thể làm đồng bằng kerberos và không bận tâm với LDAP.

+0

Có, chỉ xác minh bằng chứng xác thực. Tôi đã chỉnh sửa câu hỏi bằng cách làm rõ. Mã có khác biệt với LDAP auth không? –

6

Tôi vừa hoàn thành một dự án sử dụng AD và Java. Chúng tôi đã sử dụng Spring ldapTemplate.

AD tuân thủ LDAP (gần như), tôi không nghĩ rằng bạn sẽ gặp bất kỳ vấn đề nào với nhiệm vụ bạn có. Tôi có nghĩa là thực tế rằng nó là AD hoặc bất kỳ máy chủ LDAP khác không quan trọng nếu bạn chỉ muốn kết nối.

Tôi sẽ xem: Spring LDAP

Chúng cũng có các ví dụ.

Đối với mã hóa, chúng tôi đã sử dụng kết nối SSL (vì vậy đó là LDAPS). AD phải được cấu hình trên một cổng/giao thức SSL.

Nhưng trước hết, hãy đảm bảo bạn có thể kết nối đúng cách với AD của mình thông qua IDE LDAP. Tôi sử dụng Apache Directory Studio, nó thực sự tuyệt vời và được viết bằng Java. Đó là tất cả những gì tôi cần. Đối với mục đích thử nghiệm, bạn cũng có thể cài đặt Apache Directory Server

+0

Luchiani, tôi hiện đang phát triển ứng dụng web để tích hợp java spring để chia sẻ điểm (windows), trong khi tôi không thể tạo user trong thư mục active bằng mã java, bạn có thể chia sẻ code của mình để tạo user trong thư mục active với comment để tôi có thể tiếp tục công việc đúng giờ. –

90

Có 3 giao thức xác thực có thể được sử dụng để thực hiện xác thực giữa Java và Active Directory trên Linux hoặc bất kỳ nền tảng khác (và đây không phải là chỉ cụ thể với các dịch vụ HTTP):

  1. Kerberos - Kerberos cung cấp đăng nhập một lần (SSO) và ủy quyền nhưng máy chủ web cũng cần hỗ trợ SPNEGO để chấp nhận SSO thông qua IE.

  2. NTLM - NTLM hỗ trợ SSO qua IE (và các trình duyệt khác nếu chúng được định cấu hình đúng).

  3. LDAP - Một liên kết LDAP có thể được sử dụng để chỉ xác thực tên tài khoản và mật khẩu.

Còn có tên gọi là "ADFS"

Mỗi giao thức có lợi thế của nó nhưng theo quy tắc chung, để có khả năng tương thích tối đa, bạn thường nên cố gắng "làm như Windows". Vậy Windows làm gì? Trước tiên, xác thực giữa hai máy Windows ưa chuộng Kerberos vì máy chủ không cần giao tiếp với DC và khách hàng có thể lưu trữ vé Kerberos để giảm tải trên DC (và vì Kerberos hỗ trợ ủy quyền).

Nhưng nếu các bên xác thực không có cả hai tài khoản miền hoặc nếu khách hàng không thể liên lạc với DC, thì NTLM là bắt buộc. Vì vậy, Kerberos và NTLM không loại trừ lẫn nhau và NTLM không bị lỗi thời bởi Kerberos. Trong thực tế trong một số cách NTLM là tốt hơn so với Kerberos. Lưu ý rằng khi đề cập đến Kerberos và NTLM trong cùng một hơi thở, tôi cũng phải đề cập đến SPENGO và Xác thực Windows Tích hợp (IWA). IWA là một thuật ngữ đơn giản về cơ bản có nghĩa là Kerberos hoặc NTLM hoặc SPNEGO để thương lượng Kerberos hoặc NTLM.

Sử dụng liên kết LDAP như một cách để xác thực bằng chứng xác thực không hiệu quả và yêu cầu SSL. Nhưng cho đến khi triển khai gần đây Kerberos và NTLM đã gặp nhiều khó khăn, do đó việc sử dụng LDAP như một dịch vụ xác thực dịch chuyển đã tồn tại. Nhưng vào thời điểm này, nó thường nên tránh. LDAP là một thư mục thông tin chứ không phải là dịch vụ xác thực. Sử dụng nó cho mục đích của nó.

Vậy làm cách nào để bạn triển khai Kerberos hoặc NTLM trong Java và trong ngữ cảnh của các ứng dụng web nói riêng?

Có một số công ty lớn như Quest Software và Centrify có các giải pháp đề cập cụ thể đến Java. Tôi thực sự không thể bình luận về chúng vì chúng là "giải pháp quản lý danh tính" toàn công ty, do đó, từ việc tìm kiếm tiếp thị trên trang web của họ, thật khó để biết chính xác giao thức nào đang được sử dụng và cách thức. Bạn sẽ cần phải liên hệ với họ để biết chi tiết.

Triển khai Kerberos trong Java không quá khó khăn vì các thư viện Java chuẩn hỗ trợ Kerberos thông qua các lớp org.ietf.gssapi. Tuy nhiên, cho đến gần đây đã có một trở ngại lớn - IE không gửi các mã thông báo Kerberos thô, nó sẽ gửi các thẻ SPNEGO. Nhưng với Java 6, SPNEGO đã được thực hiện. Về lý thuyết, bạn có thể viết một số mã GSSAPI có thể xác thực các trình khách IE. Nhưng tôi đã không thử nó. Việc Sun thực hiện Kerberos là một bộ phim hài lỗi trong những năm qua dựa trên hồ sơ theo dõi của Sun trong lĩnh vực này, tôi sẽ không hứa hẹn gì về việc thực hiện SPENGO cho đến khi bạn có con chim đó trong tay.

Đối với NTLM, có một dự án PMNM miễn phí được gọi là JCIFS có Bộ lọc Servlet xác thực HTTP NTLM. Tuy nhiên nó sử dụng một phương pháp man-in-the-middle để xác thực các thông tin đăng nhập với một máy chủ SMB không hoạt động với NTLMv2 (mà từ từ trở thành một chính sách bảo mật miền bắt buộc). Vì lý do đó và các lý do khác, phần Bộ lọc HTTP của JCIFS được lên lịch để xóa. Lưu ý rằng có một số spin-off sử dụng JCIFS để thực hiện cùng một kỹ thuật. Vì vậy, nếu bạn thấy các dự án khác yêu cầu hỗ trợ NTLM SSO, hãy kiểm tra bản in đẹp.

Cách chính xác duy nhất để xác thực thông tin đăng nhập NTLM với Active Directory đang sử dụng cuộc gọi NetRLogonSamLogon DCERPC qua NETLOGON với Kênh bảo mật. Liệu một điều như vậy tồn tại trong Java? Vâng.Ở đây là:

http://www.ioplex.com/jespa.html

Jespa là một thực hiện 100% Java NTLM hỗ trợ NTLMv2, NTLMv1, trọn vẹn và bảo mật tùy chọn và nêu trên NETLOGON xác nhận chứng chỉ. Và nó bao gồm một bộ lọc HTTP SSO, một JAAS LoginModule, HTTP client, SASL client và server (với JNDI binding), "nhà cung cấp bảo mật" chung để tạo các dịch vụ NTLM tùy chỉnh và hơn thế nữa.

Mike

+3

: HttpClient giờ đây hỗ trợ các lược đồ xác thực cơ bản, Digest, NTLMv1, NTLMv2, NTLM2, SNPNEGO, Kerberos. –

2

Nếu tất cả các bạn muốn làm là xác thực đối với AD sử dụng Kerberos, sau đó một chương trình đơn giản http://spnego.sourceforge.net/HelloKDC.java nên làm điều đó.

Hãy xem tài liệu "trước chuyến bay" của dự án nói về chương trình HelloKDC.java.

4

Như ioplex và những người khác đã nói, có nhiều tùy chọn. Để xác thực bằng LDAP (và API LDAP Novell), tôi đã sử dụng một cái gì đó như:


LDAPConnection connection = new LDAPConnection(new LDAPJSSEStartTLSFactory()); 
connection.connect(hostname, port); 
connection.startTLS(); 
connection.bind(LDAPConnection.LDAP_V3, username+"@"+domain, password.getBytes()); 

Là một "tính năng đặc biệt", Active Directory cho phép LDAP liên kết chống lại "user @ domain" mà không sử dụng tên phân biệt của tài khoản. Mã này sử dụng StartTLS để kích hoạt mã hóa TLS trên kết nối; phương án khác là LDAP qua SSL, không được hỗ trợ bởi máy chủ AD của tôi.

Bí quyết thực tế là định vị máy chủ và máy chủ lưu trữ; cách chính thức là sử dụng tìm kiếm bản ghi DNS SRV (dịch vụ) để định vị một nhóm máy chủ ứng cử viên, sau đó thực hiện "ping" dựa trên UDP (theo định dạng Microsoft cụ thể) để định vị máy chủ chính xác. Nếu bạn quan tâm, tôi đã đăng một số blog articles về hành trình phiêu lưu và khám phá của tôi trong khu vực đó.

Nếu bạn muốn thực hiện xác thực tên người dùng/mật khẩu dựa trên Kerberos, bạn đang xem một loại cá khác; nó có thể thực hiện được với mã GSS-API Java, mặc dù tôi không chắc chắn nó thực hiện bước cuối cùng để xác thực xác thực. (Mã thực hiện xác nhận có thể liên hệ với máy chủ AD để kiểm tra tên người dùng và mật khẩu, dẫn đến vé cấp vé cho người dùng, nhưng để đảm bảo máy chủ AD không bị mạo danh, nó cũng cần cố gắng lấy vé Nếu bạn muốn đăng nhập một lần dựa trên Kerberos, giả sử người dùng của bạn được xác thực với miền, bạn cũng có thể làm điều đó với Java GSS-API. mã. Tôi sẽ đăng một mẫu mã, nhưng tôi vẫn cần phải biến mẫu thử nghiệm ghê gớm của mình thành thứ gì đó phù hợp với mắt người. Hãy xem some code from SpringSource để biết một số nguồn cảm hứng.

Nếu bạn đang tìm kiếm NTLM (mà tôi đã được cung cấp để hiểu là ít an toàn hơn) hoặc cái gì khác, tốt, chúc may mắn.

+0

Các mục nhập blog rất hữu ích. Cảm ơn bạn! – Aerse

+0

"user @ domain" hoạt động tốt, thx. – chris

0

Tôi khuyên bạn nên xem gói phần mềm quảng cáo của dự án oVirt. Nó sử dụng Spring-Ldap và mô-đun đăng nhập Krb5 JAAS (với GSSAPI) để xác thực bằng Kerberos đối với các máy chủ Ldap (Active-Directory, ipa, rhds, Tivoli-DS).Tìm kiếm mã tại engine \ backend \ manager \ modules \ bll \ src \ main \ java \ org \ ovirt \ engine \ core \ bll \ adbroker

Bạn có thể sử dụng git để sao chép kho lưu trữ hoặc duyệt bằng liên kết gerrit

Các vấn đề liên quan