在 Java 应用程序中使用 NTLM 身份验证
Posted
技术标签:
【中文标题】在 Java 应用程序中使用 NTLM 身份验证【英文标题】:Using NTLM authentication in Java applications 【发布时间】:2013-02-07 20:49:11 【问题描述】:我想在我的 Java 应用程序中使用 Windows NTLM 身份验证来透明地对 Intranet 用户进行身份验证。如果使用他们的浏览器(单点登录),用户应该不会注意到任何身份验证。
我找到了一些支持 NTLM 的库,但不知道该使用哪一个:
http://spnego.sourceforge.net/ http://sourceforge.net/projects/ntlmv2auth/ http://jcifs.samba.org/ http://www.ioplex.com/jespa.html http://www.luigidragone.com/software/ntlm-authentication-in-java/有什么建议从哪里开始吗?
【问题讨论】:
另外请注意,在使用 NTLM 进行身份验证时,主动攻击者可以authenticate their own session 使用有效用户与服务器的协商。 【参考方案1】:在上面的列表中,只有 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)。
【讨论】:
Waffle 也是一个很好的选择,只用于 WIndows 服务器,已经使用了 3 年,每天在多个站点的数千次登录没有问题,支持 v2 jcifs.samba.org/src/docs/faq.html#ntlmv2 >问:jCIFS 是否支持 NTLMv2? >答:是的。从 1.3.0 开始,JCIFS 完全支持 NTLMv2 并默认使用它。`Note: The NTLM HTTP SSO Filter that used to be included with JCIFS cannot support NTLMv2.
【参考方案2】:
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);
如果您对每次握手的内容感到好奇,您可以在thread 上找到另一个使用 jcifs 和 Socket 的示例。
【讨论】:
你能把那个URL部分说清楚一点吗,我的意思是如何传递用户名和密码 %5C 是反斜杠。例如 h-t-t-p://CORP\username:password@127.0.0.1/ 第一种方法给我带来了复杂 url 的问题,但注释掉 Method2 在 Windows Server 2012 上使用 Java 1.7 和 jcifs 1.3.17 对 IIS 效果很好。【参考方案3】:最近不得不在工作中实现这一点,因此这里是使用 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:输入用户名没有域很重要,否则它不起作用。就像您的域是公司名/用户名一样,人们通常只是将整个内容作为用户名输入,而您应该分别输入它们,其中 domain="companyName" 和 username="username"
【讨论】:
【参考方案4】:参考:https://jcifs.samba.org/src/docs/faq.html#ntlmv2
问:jCIFS 是否支持 NTLMv2?答:是的。从 1.3.0 开始,JCIFS 完全支持 NTLMv2 并默认使用它。
注意:以前包含在 JCIFS 中的 NTLM HTTP SSO 过滤器不支持 NTLMv2。
【讨论】:
【参考方案5】:相对于你给出的列表,我会选择JCIFS。 这个库很成熟,他们的文档很好。 最重要的是,他们有相当定期的发布,最后一个是 2011 年 11 月。
Personal Experience
: 与我尝试过的其他产品(spnego 和 ntmv2auth)相比,它相当容易上手
【讨论】:
JCIFS 不支持 NTLMv2(即 Windows 7/8),除非通过全局策略或注册表更改在客户端计算机上明确启用。 您能否澄清一下您所说的 Windows 7/8 中的显式设置是什么意思? @MiroslavLigas。我认为他的意思是 Win 7/8 不再使用 NTLMv1,因为它已被弃用并被认为对漏洞开放。现在,如果 JCIFS 仅支持 NTLMv1,那么您需要强制您的 Win 7/8 桌面允许使用 NTLMv1(通过更改 Win 注册表来完成),以便此类桌面与 JCIFS 一起使用。在大多数公司中,管理员永远不会允许这样的更改。出于所有实际目的,NTLMv1 已死! "JCIFS 使用加密技术,包括 RC4 128(用于 NTLMv2)和 AES 256(用于 Kerberos)进行身份验证、数字签名和加密。假设使用加密技术并从美国出口到其他国家的产品获得出口分类。”是我在jcifs.samba.org 的第一页上找到的...并且该条目是从 2009 年开始的,所以我根据 Tony 的假设将其称为 $hit。不过我从来没有测试过... @thecarpy - JCIFS 使用 NTLMv2 作为 CIFS 客户端。 JCIFS 中的 HTTP 位从不支持 NTLMv2,也永远不会。 JCIFS 中的所有 HTTP 内容均已弃用并将被删除。以上是关于在 Java 应用程序中使用 NTLM 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
ntlm 身份验证的 Java URLConnection 错误,但仅在 Linux 和 Java 7 上
在 .NET Core 的 Web 请求中使用 NTLM 身份验证