第四章 HTTP认证

HttpClient提供对由HTTP标准规范定义的认证模式的完全支持。HttpClient的认证框架可以扩展支持非标准的认证模式,比如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指定的实现,它包含了除了用户名/密码对外,一组额外的Windows指定的属性,比如用户域名的名字,比如在微软的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 认证模式

AuthScheme接口代表了抽象的,面向挑战-响应的认证模式。一个认证模式期望支持如下的功能:
  • 解析和处理由目标服务器在对受保护资源请求的响应中发回的挑战。
  • 提供处理挑战的属性:认证模式类型和它的参数,如果可用,比如这个认证模型可应用的领域。
  • 对给定的凭证组和HTTP请求对响应真实认证挑战生成认证字符串。
要注意认证模式可能是有状态的,涉及一系列的挑战-响应交流。HttpClient附带了一些AuthScheme实现:
  • Basic(基本):Basic认证模式定义在RFC 2617中。这个认证模式是不安全的,因为凭据以明文形式传送。尽管它不安全,如果用在和TLS/SSL加密的组合中,Basic认证模式是完全够用的。
  • Digest(摘要):Digest认证模式定义在RFC 2617中。Digest认证模式比Basic有显著的安全提升,对不想通过TLS/SL加密在完全运输安全上开销的应用程序来说也是很好的选择。
  • NTLM:NTLM是一个由微软开发的优化Windows平台的专有认证模式。NTLM被认为是比Digest更安全的模式。这个模式需要外部的NTLM引擎来工作。要获取更多详情请参考包含在HttpClient发布包中的NTLM_SUPPORT.txt文档。

4.3 HTTP认证参数

有一些可以用于定制HTTP认证过程和独立认证模式行为的参数:
  • 'http.protocol.handle-authentication':定义了是否认证应该被自动处理。这个参数期望的得到一个java.lang.Boolean类型的值。如果这个参数没有被设置,HttpClient将会自动处理认证。
  • 'http.auth.credential-charset':定义了当编码用户凭证时使用的字符集。这个参数期望得到一个java.lang.String类型的值。如果这个参数没有被设置,那么就会使用US-ASCII。

4.4 认证模式注册表

HttpClient使用AuthSchemeRegistry类维护一个可用的认证模式的注册表。对于每个默认的下面的模式是注册过的:
  • Basic:基本认证模式
  • Digest:摘要认证模式
请注意NTLM模式没有对每个默认的进行注册。NTLM不能对每个默认开启是应为许可和法律上的原因。要获取更详细的关于如何开启NTLM支持的内容请看这部分。

4.5 凭据提供器

凭据提供器意来维护一组用户凭据,还有能够对特定认证范围生产用户凭据。认证范围包括主机名,端口号,领域名称和认证模式名称。当使用凭据提供器来注册凭据时,我们可以提供一个通配符(任意主机,任意端口,任意领域,任意模式)来替代确定的属性值。如果直接匹配没有发现,凭据提供器期望被用来发现最匹配的特定范围。

HttpClient可以和任意实现了CredentialsProvider接口的凭据提供器的物理代表一同工作。默认的CredentialsProvider实现被称为BasicCredentialsProvider,它是简单的凭借java.util.HashMap的实现。
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.6 HTTP认证和执行上下文

HttpClient依赖于AuthState类来跟踪关于认证过程状态的详细信息。在HTTP请求执行过程中,HttpClient创建2个AuthState的实例:一个对于目标主机认证,另外一个对于代理认证。如果目标服务器或代理需要用户认证,那么各自的AuthState实例将会被在认证处理过程中使用的AuthScope,AuthScheme和Crednetials来填充。AuthState可以被检查来找出请求的认证是什么类型的,是否匹配AuthScheme的实现,是否凭据提供器对给定的认证范围去找用户凭据。

在HTTP请求执行的过程中,HttpClient添加了下列和认证相关的对象到执行上下文中:

  • 'http.authscheme-registry':AuthSchemeRegistry实例代表真实的认证模式注册表。在本地内容中设置的这个属性的值优先于默认的。
  • 'http.auth.credentials-provider':CookieSpec实例代表了真实的凭据提供器。在本地内容中设置的这个属性的值优先于默认的。
  • 'http.auth.target-scope':AuthState实例代表了真实的目标认证状态。在本地内容中设置的这个属性的值优先于默认的。
  • 'http.auth.proxy-scope':AuthState实例代表了真实的代理认证状态。在本地内容中设置的这个属性的值优先于默认的。

本地的HttpContext对象可以用于定制HTTP认证内容,并先于请求执行或在请求被执行之后检查它的状态:

HttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/");
HttpResponse response = httpclient.execute(httpget, localContext);
AuthState proxyAuthState = (AuthState) localContext.getAttribute(
ClientContext.PROXY_AUTH_STATE);
System.out.println("Proxy auth scope: " + proxyAuthState.getAuthScope());
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
AuthState targetAuthState = (AuthState) localContext.getAttribute(
ClientContext.TARGET_AUTH_STATE);
System.out.println("Target auth scope: " + targetAuthState.getAuthScope());
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());

4.7 抢占认证

HttpClient不支持开箱的抢占认证,因为滥用或重用不正确的抢占认证可能会导致严重的安全问题,比如将用户凭据以明文形式发送给未认证的第三方。因此,用户期望评估抢占认证和在它们只能应用程序环境内容安全风险潜在的好处,而且要求使用如协议拦截器的标准HttpClient扩展机制添加对抢占认证的支持。

这是一个简单的协议拦截器,如果没有企图认证,来抢先引入BasicScheme的实例到执行上下文中。请注意拦截器必须在标准认证拦截器之前加入到协议处理链中。

HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {
public void process(final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(
ClientContext.TARGET_AUTH_STATE);
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
// 如果没有初始化auth模式
if (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(
targetHost.getHostName(),
targetHost.getPort());
// 获得匹配目标主机的凭据
Credentials creds = credsProvider.getCredentials(authScope);
// 如果发现了,抢先生成BasicScheme
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
};
DefaultHttpClient httpclient = new DefaultHttpClient();
// 作为第一个拦截器加入到协议链中
httpclient.addRequestInterceptor(preemptiveAuth, 0);

4.8 NTLM 认证

当前HttpClient没有提对开箱的NTLM认证模式的支持也可能永远也不会。这个原因是法律上的而不是技术上的。然而,NTLM认证可以使用外部的NTLM引擎比如JCIFS[http://jcifs.samba.org/]来开启,类库由Samba[http://www.samba.org/]项目开发,作为它们Windows的交互操作程序套装的一部分。要获取详细内容请参考HttpClient发行包中包含的NTLM_SUPPORT.txt文档。

4.8.1 NTLM连接持久化

NTLM认证模式是在计算开销方面昂贵的多的,而且对标准的Basic和Digest模式的性能影响也很大。这很可能是为什么微软选择NTLM认证模式为有状态的主要原因之一。也就是说,一旦认证通过,用户标识是和连接的整个生命周期相关联的。NTLM连接的状态特性使得连接持久化非常复杂,对于明显的原因,持久化NTLM连接不能被使用不同用户标识的用户重用。标准的连接管理器附带HttpClient是完全能够管理状态连接的。而逻辑相关的,使用同一session和执行上下文为了让它们了解到当前的用户标识的请求也是极为重要的。否则,HttpClient将会终止对每个基于NTLM保护资源的HTTP请求创建新的HTTP连接。要获取关于有状态的HTTP连接的详细讨论,请参考这个部分。

因为NTLM连接是有状态的,通常建议使用相对简单的方法触发NTLM认证,比如GET或HEAD,而重用相同的连接来执行代价更大的方法,特别是它们包含请求实体,比如POST或PUT。

DefaultHttpClient httpclient = new DefaultHttpClient();
NTCredentials creds = new NTCredentials("user", "pwd", "myworkstation", "microsoft.com");
httpclient.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);
HttpHost target = new HttpHost("www.microsoft.com", 80, "http");
// 保证相同的内容来用于执行逻辑相关的请求
HttpContext localContext = new BasicHttpContext();
// 首先执行简便的方法。这会触发NTLM认证
HttpGet httpget = new HttpGet("/ntlm-protected/info");
HttpResponse response1 = httpclient.execute(target, httpget, localContext);
HttpEntity entity1 = response1.getEntity();
if (entity1 != null) {
entity1.consumeContent();
}
//之后使用相同的内容(和连接)执行开销大的方法。
HttpPost httppost = new HttpPost("/ntlm-protected/form");
httppost.setEntity(new StringEntity("lots and lots of data"));
HttpResponse response2 = httpclient.execute(target, httppost, localContext);
HttpEntity entity2 = response2.getEntity();
if (entity2 != null) {
entity2.consumeContent();
}

HttpClient 教程 (四)的更多相关文章

  1. CRL快速开发框架系列教程四(删除数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  2. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(四)-使用Travis自动部署Hexo(2)

    前言 前面一篇文章介绍了Travis自动部署Hexo的常规使用教程,也是个人比较推荐的方法. 前文最后也提到了在Windows系统中可能会有一些小问题,为了在Windows系统中也可以实现使用Trav ...

  3. C#微信公众号开发系列教程四(接收普通消息)

    微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...

  4. 无废话ExtJs 入门教程四[表单:FormPanel]

    无废话ExtJs 入门教程四[表单:FormPanel] extjs技术交流,欢迎加群(201926085) 继上一节内容,我们在窗体里加了个表单.如下所示代码区的第28行位置,items:form. ...

  5. TFS(Team Foundation Server)敏捷使用教程(四):工作项跟踪(1)

    工作项跟踪(1) 可跟踪性是软件过程的重要能力,TFS主要是以工作项来实现过程的可跟踪性.曾有人问:"你们实际项目里的工作项是怎么样的?能不能让我们看看?"我也一直很好奇别的公司T ...

  6. Android Studio系列教程四--Gradle基础

    Android Studio系列教程四--Gradle基础 2014 年 12 月 18 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://stormzhang ...

  7. Laravel教程 四:数据库和Eloquent

    Laravel教程 四:数据库和Eloquent 此文章为原创文章,未经同意,禁止转载. Eloquent Database 上一篇写了一些Laravel Blade的基本用法和给视图传递变量的几种方 ...

  8. NGUI系列教程四(自定义Atlas,Font)

    今天我们来看一下怎么自定义NGUIAtlas,制作属于自己风格的UI.第一部分:自定义 Atlas1 . 首先我们要准备一些图标素材,也就是我们的UI素材,将其导入到unity工程中.2. 全选我们需 ...

  9. Qt零基础教程(四) QWidget详解篇

    在博客园里面转载我自己写的关于Qt的基础教程,没次写一篇我会在这里更新一下目录: Qt零基础教程(四) QWidget详解(1):创建一个窗口 Qt零基础教程(四) QWidget详解(2):QWid ...

随机推荐

  1. MySQL常见架构的应用

    MySQL 的架构设计 MySQL 架构一定要结合前台业务来设计.优化,所以不管是哪种架构.根据业务要求组合成符合需求的即是最好的.不能泛泛而谈同时.也必须注意数据的安全(如ipsec,ssh,vpn ...

  2. 解决openssh TimeOut

    SSH Client:ServerAliveInterval 100 SSH server:ClientAliveInterval 30TCPKeepAlive yes ClientAliveCoun ...

  3. java SequenceInputStream类(序列输入流)的用法示例

    public class SequenceInputStreamextends InputStream SequenceInputStream 表示其他输入流的逻辑串联.它从输入流的有序集合开始,并从 ...

  4. 生产服务器环境最小化安装后 Centos 6.5优化配置备忘

    生产服务器环境最小化安装后 Centos 6.5优化配置备忘 作者:Memory 发布于:2014-8-13 15:00 Wednesday 服务器 本文 centos 6.5 优化 的项有18处: ...

  5. Loadrunner脚本编程(4)-数据类型操作和字符串操作

    http://www.360doc.com/content/10/0806/13/1698198_44078277.shtml 一,数据类型转换 没有使用过C编程的LoadRunner脚本编写者会发现 ...

  6. 6款国内、国外开源PHP轻论坛CMS程序

    随着移动互联网对于传统互联网的冲击,用户群更加注重信息的及时性和有效性的简便分享和获取,传统的社区模式经过多年的积累沉淀很深,尤其对于新兴的社区用户群和站长来说,如果需要挑战目前已经非常成熟的社区群还 ...

  7. 深入PHP内核之全局变量

    在阅读PHP源码的时候,会遇到很多诸如:CG(),EG() ,PG(),FG()这样的宏,如果不了解这些宏的意义,会给理解源码造成很大困难 EG().这个宏可以用来访问符号表,函数,资源信息和常量 C ...

  8. PHP中的安全函数

    安全是编程非常重要的一个方面.在任何一种编程语言中,都提供了许多的函数或者模块来确保程序的安全性.在现代网站应用中,经常要获取来自世界各地用户的输入,但是,我们都知道“永远不能相信那些用户输入的数据” ...

  9. HDUOJ----2063过山车

    过山车 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  10. HDUOJ-------Being a Good Boy in Spring Festival

    Being a Good Boy in Spring Festival Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32 ...