在 Java 应用程序中使用 NTLM 身份验证

2022-09-03 01:58:16

我想在我的 Java 应用程序中使用 Windows NTLM 身份验证来透明地对 Intranet 用户进行身份验证。如果使用浏览器(单点登录),用户不应注意到任何身份验证。

我发现了一些支持NTLM的库,但不知道该使用哪一个:

有什么建议从哪里开始吗?


答案 1

在上面的列表中,只有ntlmv2-auth和Jespa支持NTLMv2。ntlmv2-auth我还没有尝试过,但它是基于Liferay的代码,我以前见过它的工作原理。

“ntlm-authentication-in-java”只是NTLMv1,它是旧的,不安全的,并且随着人们升级到较新的Windows版本,可以在越来越多的环境中工作。JCIFS曾经有一个NTLMv1 HTTP身份验证过滤器,但在后来的版本中被删除了,因为它的实现方式相当于对不安全协议的中间人攻击。(“ntlm-authentication-in-java”似乎也是如此。

“spnego”项目是Kerberos而不是NTLM。如果你想像IIS那样复制完整的IWA,你需要同时支持NTLMv2和Kerberos('NTLM'auth,'Negotiate'auth,NTLMSSP-in-SPNego auth和NTLM-masquerading-as-Negotiate auth)。


答案 2

路易吉·德拉贡的剧本真的很旧,似乎总是失败。

HttpURLConnection可以使用NTLM,如果你添加库jcifs,这个例子适用于最新的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找到另一个示例。