HttpClient4.3教程 第四章 HTTP认证
HttpClient既支持HTTP标准规范定义的认证模式,又支持一些广泛使用的非标准认证模式,比如NTLM和SPNEGO。
4.1.用户凭证
任何用户认证的过程,都需要一系列的凭证来确定用户的身份。最简单的用户凭证可以是用户名和密码这种形式。UsernamePasswordCredentials
这个类可以用来表示这种情况,这种凭据包含明文的用户名和密码。
这个类对于HTTP标准规范中定义的认证模式来说已经足够了。
- UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");
- System.out.println(creds.getUserPrincipal().getName());
- System.out.println(creds.getPassword());
上述代码会在控制台输出:
- user
- pwd
NTCredentials
是微软的windows系统使用的一种凭据,包含username、password,还包括一系列其他的属性,比如用户所在的域名。在Microsoft Windows的网络环境中,同一个用户可以属于不同的域,所以他也就有不同的凭据。
- NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");
- System.out.println(creds.getUserPrincipal().getName());
- System.out.println(creds.getPassword());
上述代码输出:
- DOMAIN/user
- pwd
4.2. 认证方案
AutoScheme
接口表示一个抽象的面向挑战/响应的认证方案。一个认证方案要支持下面的功能:
- 客户端请求服务器受保护的资源,服务器会发送过来一个chanllenge(挑战),认证方案(Authentication scheme)需要解析、处理这个挑战
- 为processed challenge提供一些属性值:认证方案的类型,和此方案需要的一些参数,这种方案适用的范围
- 使用给定的授权信息生成授权字符串;生成http请求,用来响应服务器发送来过的授权challenge
请注意:一个认证方案可能是有状态的,因为它可能涉及到一系列的挑战/响应。
HttpClient实现了下面几种AutoScheme
:
- Basic: Basic认证方案是在RFC2617号文档中定义的。这种授权方案用明文来传输凭证信息,所以它是不安全的。虽然Basic认证方案本身是不安全的,但是它一旦和TLS/SSL加密技术结合起来使用,就完全足够了。
- Digest: Digest(摘要)认证方案是在RFC2617号文档中定义的。Digest认证方案比Basic方案安全多了,对于那些受不了Basic+TLS/SSL传输开销的系统,digest方案是个不错的选择。
- NTLM: NTLM认证方案是个专有的认证方案,由微软开发,并且针对windows平台做了优化。NTLM被认为比Digest更安全。
- SPNEGO: SPNEGO(Simple and Protected GSSAPI Negotiation Mechanism)是GSSAPI的一个“伪机制”,它用来协商真正的认证机制。SPNEGO最明显的用途是在微软的HTTP协商认证机制拓展上。可协商的子机制包括NTLM、Kerberos。目前,HttpCLient只支持Kerberos机制。(原文:The negotiable sub-mechanisms include NTLM and Kerberos supported by Active Directory. At present HttpClient only supports the Kerberos sub-mechanism.)
4.3. 凭证 provider
凭证providers旨在维护一套用户的凭证,当需要某种特定的凭证时,providers就应该能产生这种凭证。认证的具体内容包括主机名、端口号、realm name和认证方案名。当使用凭据provider的时候,我们可以很模糊的指定主机名、端口号、realm和认证方案,不用写的很精确。因为,凭据provider会根据我们指定的内容,筛选出一个最匹配的方案。
只要我们自定义的凭据provider实现了CredentialsProvider
这个接口,就可以在HttpClient中使用。默认的凭据provider叫做BasicCredentialsProvider
,它使用java.util.HashMap
对CredentialsProvider
进行了简单的实现。
- CredentialsProvider credsProvider = new BasicCredentialsProvider();
- credsProvider.setCredentials(
- new AuthScope("somehost", AuthScope.ANY_PORT),
- new UsernamePasswordCredentials("u1", "p1"));
- credsProvider.setCredentials(
- new AuthScope("somehost", 8080),
- new UsernamePasswordCredentials("u2", "p2"));
- credsProvider.setCredentials(
- new AuthScope("otherhost", 8080, AuthScope.ANY_REALM, "ntlm"),
- new UsernamePasswordCredentials("u3", "p3"));
- System.out.println(credsProvider.getCredentials(
- new AuthScope("somehost", 80, "realm", "basic")));
- System.out.println(credsProvider.getCredentials(
- new AuthScope("somehost", 8080, "realm", "basic")));
- System.out.println(credsProvider.getCredentials(
- new AuthScope("otherhost", 8080, "realm", "basic")));
- System.out.println(credsProvider.getCredentials(
- new AuthScope("otherhost", 8080, null, "ntlm")));
上面代码输出:
- [principal: u1]
- [principal: u2]
- null
- [principal: u3]
4.4.HTTP授权和执行上下文
HttpClient依赖AuthState
类去跟踪认证过程中的状态的详细信息。在Http请求过程中,HttpClient创建两个AuthState
实例:一个用于目标服务器认证,一个用于代理服务器认证。如果服务器或者代理服务器需要用户的授权信息,AuthScope
、AutoScheme
和认证信息就会被填充到两个AuthScope
实例中。通过对AutoState
的检测,我们可以确定请求的授权类型,确定是否有匹配的AuthScheme
,确定凭据provider根据指定的授权类型是否成功生成了用户的授权信息。
在Http请求执行过程中,HttpClient会向执行上下文中添加下面的授权对象:
Lookup
对象,表示使用的认证方案。这个对象的值可以在本地上下文中进行设置,来覆盖默认值。CredentialsProvider
对象,表示认证方案provider,这个对象的值可以在本地上下文中进行设置,来覆盖默认值。AuthState
对象,表示目标服务器的认证状态,这个对象的值可以在本地上下文中进行设置,来覆盖默认值。AuthState
对象,表示代理服务器的认证状态,这个对象的值可以在本地上下文中进行设置,来覆盖默认值。AuthCache
对象,表示认证数据的缓存,这个对象的值可以在本地上下文中进行设置,来覆盖默认值。
我们可以在请求执行前,自定义本地HttpContext
对象来设置需要的http认证上下文;也可以在请求执行后,再检测HttpContext
的状态,来查看授权是否成功。
- CloseableHttpClient httpclient = <...>
- CredentialsProvider credsProvider = <...>
- Lookup<AuthSchemeProvider> authRegistry = <...>
- AuthCache authCache = <...>
- HttpClientContext context = HttpClientContext.create();
- context.setCredentialsProvider(credsProvider);
- context.setAuthSchemeRegistry(authRegistry);
- context.setAuthCache(authCache);
- HttpGet httpget = new HttpGet("https://www.yeetrack.com/");
- CloseableHttpResponse response1 = httpclient.execute(httpget, context);
- <...>
- AuthState proxyAuthState = context.getProxyAuthState();
- System.out.println("Proxy auth state: " + proxyAuthState.getState());
- System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
- System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
- AuthState targetAuthState = context.getTargetAuthState();
- System.out.println("Target auth state: " + targetAuthState.getState());
- System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
- System.out.println("Target auth credentials: " + targetAuthState.getCredentials());
4.5. 缓存认证数据
从版本4.1开始,HttpClient就会自动缓存验证通过的认证信息。但是为了使用这个缓存的认证信息,我们必须在同一个上下文中执行逻辑相关的请求。一旦超出该上下文的作用范围,缓存的认证信息就会失效。
4.6. 抢先认证
HttpClient默认不支持抢先认证,因为一旦抢先认证被误用或者错用,会导致一系列的安全问题,比如会把用户的认证信息以明文的方式发送给未授权的第三方服务器。因此,需要用户自己根据自己应用的具体环境来评估抢先认证带来的好处和带来的风险。
即使如此,HttpClient还是允许我们通过配置来启用抢先认证,方法是提前填充认证信息缓存到上下文中,这样,以这个上下文执行的方法,就会使用抢先认证。
- CloseableHttpClient httpclient = <...>
- HttpHost targetHost = new HttpHost("localhost", 80, "http");
- CredentialsProvider credsProvider = new BasicCredentialsProvider();
- credsProvider.setCredentials(
- new AuthScope(targetHost.getHostName(), targetHost.getPort()),
- new UsernamePasswordCredentials("username", "password"));
- // 创建 AuthCache 对象
- AuthCache authCache = new BasicAuthCache();
- //创建 BasicScheme,并把它添加到 auth cache中
- BasicScheme basicAuth = new BasicScheme();
- authCache.put(targetHost, basicAuth);
- // 把AutoCache添加到上下文中
- HttpClientContext context = HttpClientContext.create();
- context.setCredentialsProvider(credsProvider);
- HttpGet httpget = new HttpGet("/");
- for (int i = 0; i < 3; i++) {
- CloseableHttpResponse response = httpclient.execute(
- targetHost, httpget, context);
- try {
- HttpEntity entity = response.getEntity();
- } finally {
- response.close();
- }
- }
4.7. NTLM认证
从版本4.1开始,HttpClient就全面支持NTLMv1、NTLMv2和NTLM2认证。当人我们可以仍旧使用外部的NTLM引擎(比如Samba开发的JCIFS库)作为与Windows互操作性程序的一部分。
4.7.1. NTLM连接持久性
相比Basic
和Digest
认证,NTLM认证要明显需要更多的计算开销,性能影响也比较大。这也可能是微软把NTLM协议设计成有状态连接的主要原因之一。也就是说,NTLM连接一旦建立,用户的身份就会在其整个生命周期和它相关联。NTLM连接的状态性使得连接持久性更加复杂,The stateful nature of NTLM connections makes connection persistence more complex, as for the obvious reason persistent NTLM connections may not be re-used by users with a different user identity. HttpClient中标准的连接管理器就可以管理有状态的连接。但是,同一会话中逻辑相关的请求,必须使用相同的执行上下文,这样才能使用用户的身份信息。否则,HttpClient就会结束旧的连接,为了获取被NTLM协议保护的资源,而为每个HTTP请求,创建一个新的Http连接。更新关于Http状态连接的信息,点击此处。
由于NTLM连接是有状态的,一般推荐使用比较轻量级的方法来处罚NTLM认证(如GET、Head方法),然后使用这个已经建立的连接在执行相对重量级的方法,尤其是需要附件请求实体的请求(如POST、PUT请求)。
- CloseableHttpClient httpclient = <...>
- CredentialsProvider credsProvider = new BasicCredentialsProvider();
- credsProvider.setCredentials(AuthScope.ANY,
- new NTCredentials("user", "pwd", "myworkstation", "microsoft.com"));
- HttpHost target = new HttpHost("www.microsoft.com", 80, "http");
- //使用相同的上下文来执行逻辑相关的请求
- HttpClientContext context = HttpClientContext.create();
- context.setCredentialsProvider(credsProvider);
- //使用轻量级的请求来触发NTLM认证
- HttpGet httpget = new HttpGet("/ntlm-protected/info");
- CloseableHttpResponse response1 = httpclient.execute(target, httpget, context);
- try {
- HttpEntity entity1 = response1.getEntity();
- } finally {
- response1.close();
- }
- //使用相同的上下文,执行重量级的方法
- HttpPost httppost = new HttpPost("/ntlm-protected/form");
- httppost.setEntity(new StringEntity("lots and lots of data"));
- CloseableHttpResponse response2 = httpclient.execute(target, httppost, context);
- try {
- HttpEntity entity2 = response2.getEntity();
- } finally {
- response2.close();
- }
4.8. SPNEGO/Kerberos认证
SPNEGO(Simple and Protected GSSAPI Megotiation Mechanism),当双方均不知道对方能使用/提供什么协议的情况下,可以使用SP认证协议。这种协议在Kerberos认证方案中经常使用。It can wrap other mechanisms, however the current version in HttpClient is designed solely with Kerberos in mind.
4.8.1. 在HTTPCIENT中使用SPNEGO
SPNEGO认证方案兼容Sun java 1.5及以上版本。但是强烈推荐jdk1.6以上。Sun的JRE提供的类就已经几乎完全可以处理Kerberos和SPNEGO token。这就意味着,需要设置很多的GSS类。SpnegoScheme
是个很简单的类,可以用它来handle marshalling the tokens and 读写正确的头消息。
最好的开始方法就是从示例程序中找到KerberosHttpClient.java
这个文件,尝试让它运行起来。运行过程有可能会出现很多问题,但是如果人品比较高可能会顺利一点。这个文件会提供一些输出,来帮我们调试。
在Windows系统中,应该默认使用用户的登陆凭据;当然我们也可以使用kinit
来覆盖这个凭据,比如$JAVA_HOME\bin\kinit testuser@AD.EXAMPLE.NET
,这在我们测试和调试的时候就显得很有用了。如果想用回Windows默认的登陆凭据,删除kinit创建的缓存文件即可。
确保在krb5.conf文件中列出domain_realms
。这能解决很多不必要的问题。
4.8.2. 使用GSS/JAVA KERBEROS
下面的这份文档是针对Windows系统的,但是很多信息同样适合Unix。
org.ietf.jgss
这个类有很多的配置参数,这些参数大部分都在krb5.conf/krb5.ini
文件中配置。更多的信息,参考此处。
login.conf文件
下面是一个基本的login.conf文件,使用于Windows平台的IIS和JBoss Negotiation模块。
系统配置文件java.security.auth.login.config
可以指定login.conf
文件的路径。login.conf
的内容可能会是下面的样子:
- com.sun.security.jgss.login {
- com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache=true;
- };
- com.sun.security.jgss.initiate {
- com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache=true;
- };
- com.sun.security.jgss.accept {
- com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache=true;
- };
4.8.4. KRB5.CONF / KRB5.INI 文件
如果没有手动指定,系统会使用默认配置。如果要手动指定,可以在java.security.krb5.conf
中设置系统变量,指定krb5.conf
的路径。krb5.conf
的内容可能是下面的样子:
- [libdefaults]
- default_realm = AD.EXAMPLE.NET
- udp_preference_limit = 1
- [realms]
- AD.EXAMPLE.NET = {
- kdc = KDC.AD.EXAMPLE.NET
- }
- [domain_realms]
- .ad.example.net=AD.EXAMPLE.NET
- ad.example.net=AD.EXAMPLE.NET
4.8.5. WINDOWS详细的配置
为了允许Windows使用当前用户的tickets,javax.security.auth.useSubjectCredsOnly
这个系统变量应该设置成false
,并且需要在Windows注册表中添加allowtgtsessionkey
这个项,而且要allow session keys to be sent in the Kerberos Ticket-Granting Ticket.
Windows Server 2003和Windows 2000 SP4,配置如下:
- HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters
- Value Name: allowtgtsessionkey
- Value Type: REG_DWORD
- Value: 0x01
Windows XP SP2 配置如下:
- HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\
- Value Name: allowtgtsessionkey
- Value Type: REG_DWORD
- Value: 0x01
HttpClient4.3教程 第四章 HTTP认证的更多相关文章
- [Learn Android Studio 汉化教程]第四章 : Refactoring Code
[Learn Android Studio 汉化教程]第四章 : Refactoring Code 第四章 Refactoring Code 重构代码 在Android Studio中开发,解决 ...
- 2018-06-20 中文代码示例视频演示Python入门教程第四章 控制流
知乎原链 续前作: 中文代码示例视频演示Python入门教程第三章 简介Python 对应在线文档: 4. More Control Flow Tools 录制中出了不少岔子. 另外, 输入法确实是一 ...
- python 教程 第四章、 控制流
第四章. 控制流 控制语句后面要加冒号: 1) if语句 if guess == number: print 'Congratulations, you guessed it.' # New b ...
- Cobalt Strike系列教程第四章:文件/进程管理与键盘记录
Cobalt Strike系列教程分享如约而至,新关注的小伙伴可以先回顾一下前面的内容: Cobalt Strike系列教程第一章:简介与安装 Cobalt Strike系列教程第二章:Beacon详 ...
- [ABP教程]第四章 集成测试
Web应用程序开发教程 - 第三章: 集成测试 //[doc-params] { "UI": ["MVC","NG"], "DB& ...
- Flask 教程 第四章:数据库
本文翻译自 The Flask Mega-Tutorial Part IV: Database 在Flask Mega-Tutorial系列的第四部分,我将告诉你如何使用数据库. 本章的主题是重中之重 ...
- 脚本语言丨Batch入门教程第四章:调用与传参
今天是Batch入门教程的最后一章内容:调用与传参.相信通过前面的学习,大家已经掌握了Windows Batch有关的基础知识和编程方法,以及利用Windows Batch建立初级的编程思维方式.今后 ...
- java2 实用教程第四章
博主原创 转载请注明地址 博客:http://www.cnblogs.com/13224ACMer/ 1成员变量 声明变量所声明的变量被称为成员变量和域变量,成员变量在类中的书写位置与前后顺序无关, ...
- storm入门教程 第四章 消息的可靠处理【转】
4.1 简介 storm可以确保spout发送出来的每个消息都会被完整的处理.本章将会描述storm体系是如何达到这个目标的,并将会详述开发者应该如何使用storm的这些机制来实现数据的可靠处理. 4 ...
随机推荐
- C语言:猴子吃桃问题
//猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个. //第二天早上又将第一天剩下的桃子吃掉一半,有多吃了一个.以后每天早上都吃了前一天剩下的一半零一个. //到第 10 ...
- Python语言对Json对象进行新增替换操作
# Json字符串进行新增操作import jsonimport os# os.path.dirname(__file__):表示当前目录path = os.path.join(os.path.dir ...
- SpringBoot | 3.1 配置数据源
目录 前言 1. 数据源的自动配置 2. *数据源自动配置源码分析 2.1 DataSourceAutoConfiguration:数据源自动配置类 2.2 JdbcTemplateAutoConfi ...
- VM虚拟机桥接模式无法联网、NAT模式能正常联网
桥接模式:使虚拟机客户机可以和主机在同一网段,这样,和主机同局域网内的其他主机就也可以ping到虚拟机了: 因此,虚拟机设置为桥接模式,这样以后就可以方便的使用虚拟机了: 有时,虚拟机为桥接模式上不了 ...
- 简单快速安装Apache+PHP+MySql服务环境(四)—— 将php版本升级到7.2
书接上文,简单快速安装Apache+PHP+MySql服务环境(二)-- centos使用yum安装指定版本的php. 随着各种PHP框架的升级,对PHP的版本也有了更高的要求,所以笔者也尝试着更新升 ...
- 7.29考试总结(NOIP模拟27)[牛半仙的妹子图·Tree·序列]
前言 从思路上来讲是比较成功的,从分数上就比较令人失望了. 考场上是想到了前两个题的正解思路,其实最后一个题是半个原题,只可惜是我看不懂题... 这波呀,这波又是 语文素养限制OI水平.. 改题的时候 ...
- sqldbx配置连接Oracle 12C数据库
本地开发环境: Windows10 64位.Oracle 12C客户端 32位.sqlDBX (32位) =============================================== ...
- python errno库与socket.connect_ex()方法的结合使用
前言:一般socket链接会首选connect方法,该方法会一直尝试链接.那么今天展示下connect_ex()方法,该方法如果链接成功会返回0,失败会返回errno库中的errorcode中的key ...
- 教你如何使用FusionInsight SqoopShell
摘要:Sqoop-shell是一个Loader的shell工具,其所有功能都是通过执行脚本"sqoop2-shell"来实现的. 本文分享自华为云社区<FusionInsig ...
- JavaGUI输入框事件监听的使用
JavaGUI输入框事件监听的使用 package GUI; import java.awt.*; import java.awt.event.ActionEvent; import java.awt ...