16

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

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

有什么建议从哪里开始?

4

5 回答 5

10

在上面的列表中,只有 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' auth、'Negotiate' auth、NTLMSSP-in-SPNego auth 和 NTLM-masquerading-as-Negotiate auth)。

于 2013-02-22T16:04:45.463 回答
4

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 的示例。

于 2015-12-16T20:14:06.637 回答
4

最近不得不在工作中实现这一点,因此这里是使用 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-weborg.apache.httpcomponents

ps:输入用户名不带域很重要,否则不起作用。就像您的域是公司名/用户名一样,人们通常只是将整个内容作为用户名输入,而您应该分别输入它们,其中 domain="companyName" 和 username="username"

于 2021-04-20T16:07:17.103 回答
1

参考:https ://jcifs.samba.org/src/docs/faq.html#ntlmv2

问:jCIFS 是否支持 NTLMv2?
答:是的。从 1.3.0 开始,JCIFS 完全支持 NTLMv2 并默认使用它。

注意:以前包含在 JCIFS 中的 NTLM HTTP SSO 过滤器不支持 NTLMv2。

于 2017-02-22T19:06:37.660 回答
0

相对于您提供的列表,我会选择 JCIFS。这个库很成熟,他们的文档很好。最重要的是,他们有相当定期的发布,最后一个是 2011 年 11 月。

Personal Experience:与我尝试过的其他产品(spnego 和 ntmv2auth)相比,入门相当容易

于 2013-02-22T09:41:00.743 回答