[英]Using NTLM authentication in Java applications
我想在我的 Java 應用程序中使用 Windows NTLM 身份驗證來透明地對 Intranet 用戶進行身份驗證。 如果使用他們的瀏覽器(單點登錄),用戶應該不會注意到任何身份驗證。
我找到了一些支持 NTLM 的庫,但不知道該使用哪一個:
任何建議從哪里開始?
在上面的列表中,只有 ntlmv2-auth 和 Jespa 支持 NTLMv2。 Jespa 可行但商業化。 ntlmv2-auth 我沒試過,但它基於 Liferay 的代碼,我以前見過它工作。
'ntlm-authentication-in-java' 只是 NTLMv1,它是舊的、不安全的,並且隨着人們升級到更新的 Windows 版本,它在越來越少的環境中工作。 JCIFS 曾經有一個 NTLMv1 HTTP 身份驗證過濾器,但它在以后的版本中被刪除,因為它的實現方式相當於對不安全協議的中間人攻擊。 ('ntlm-authentication-in-java' 似乎也是如此。)
“spnego”項目是 Kerberos 而不是 NTLM。 如果您想像 IIS 那樣復制完整的 IWA,您需要同時支持 NTLMv2 和 Kerberos(“NTLM”身份驗證、“協商”身份驗證、NTLMSSP-in-SPNego 身份驗證和 NTLM-masquerading-as-Negotiate 身份驗證)。
Luigi Dragone 的劇本真的很舊,似乎總是失敗。
如果添加庫jcifs HttpURLConnection 可以與 NTLM一起使用,此示例適用於最新的jcifs-1.3.18 :
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.impl.auth.NTLMEngineException;
public class TestNTLMConnection {
public static void main(String[] args) throws UnknownHostException, IOException, NTLMEngineException {
// Method 1 : authentication in URL
jcifs.Config.registerSmbURLHandler();
URL urlRequest = new URL("http://domain%5Cuser:pass@127.0.0.1/");
// or Method 2 : authentication via System.setProperty()
// System.setProperty("http.auth.ntlm.domain", "domain");
// System.setProperty("jcifs.smb.client.domain", "domain");
// System.setProperty("jcifs.smb.client.username", "user");
// System.setProperty("jcifs.smb.client.password", "pass");
// Not verified // System.setProperty("jcifs.netbios.hostname", "host");
// System.setProperty("java.protocol.handler.pkgs", "jcifs");
// URL urlRequest = new URL("http://127.0.0.1:8180/simulate_get.php");
HttpURLConnection conn = (HttpURLConnection) urlRequest.openConnection();
StringBuilder response = new StringBuilder();
try {
InputStream stream = conn.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
String str = "";
while ((str = in.readLine()) != null) {
response.append(str);
}
in.close();
System.out.println(response);
} catch(IOException err) {
System.out.println(err);
} finally {
Map<String, String> msgResponse = new HashMap<String, String>();
for (int i = 0;; i++) {
String headerName = conn.getHeaderFieldKey(i);
String headerValue = conn.getHeaderField(i);
if (headerName == null && headerValue == null) {
break;
}
msgResponse.put(headerName == null ? "Method" : headerName, headerValue);
}
System.out.println(msgResponse);
}
}
}
如果你對每次握手的內容感到好奇,你可以在這個線程上找到另一個使用 jcifs 和 Socket 的例子。
最近不得不在工作中實現這一點,因此這里是使用 Spring 的 RestTemplate 更新的解決方案:
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
public class Runner {
public static void main(String[] args) {
var credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new NTCredentials("username", "password", "", "someDomain"));
try (var client = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.build();) {
var requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(client);
RestTemplate restTemplate = new RestTemplate(requestFactory);
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("url", new HttpEntity<>("yourDtoObject"), String.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}
需要的依賴是: spring-web
和org.apache.httpcomponents
ps:輸入不帶域的用戶名很重要,否則不起作用。 就像您的域是 companyName/username 通常人們只是輸入整個內容作為用戶名,您應該做的是分別輸入它們,其中 domain="companyName" 和 username="username"
相對於您提供的列表,我會選擇JCIFS 。 圖書館很成熟,他們的文檔很好。 最重要的是,他們有相當定期的發布,最后一個是 2011 年 11 月。
Personal Experience
:與我嘗試過的其他工具(spnego 和 ntmv2auth)相比,上手相當容易
參考: https : //jcifs.samba.org/src/docs/faq.html#ntlmv2
問:jCIFS 是否支持 NTLMv2?
答:是的。 從 1.3.0 開始,JCIFS 完全支持 NTLMv2 並默認使用它。
注意:曾經包含在 JCIFS 中的 NTLM HTTP SSO 過濾器不支持 NTLMv2。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.