HttpClient4.5教程-第四章-HTTP身份认证

简介: HttpClient对HTTP标准规范中定义的认证机制和非标准的认证机制如NTLM和SPNEGO提供了全面的支持。

HttpClient对HTTP标准规范中定义的认证机制和非标准的认证机制如NTLM和SPNEGO提供了全面的支持。

4.1 用户凭证

        大部分的用户认证过程都需要一组凭证用于鉴定用户的身份,用户凭证最简单的方式就是一组 用户名/密码 对,UsernamePasswordCredentials用明文形式表示一组安全主体和密码凭证,该类一般可满足HTTP标准规范中的认证机制的要求。

//安全主体user 密码凭证pwd
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());

输出:

user 
pwd 

       NTCredentials是Microsoft Windows特定的实现,包含了用户名/密码 对,附加了Windows特定的属性如用户所属的域名。在Microsoft Windows网络中同一个用户可以属于不同的域,每一个域都有不同的认证机制。

//wordstation指发出请求的机器名称,一般是电脑名,domain是域的名称
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 request生成认证字符串用于认证挑战的应答。

       请注意认证机制可能是有状态的,应该避免一系列的挑战-应答交换。

       HttpClient附带多个AuthScheme的实现:

       Basic: 如RFC2617所定义的基本认证机制,由于凭证通过明文传递,该认证机制是不安全的,尽管其安全性比较低,但是如果与TLS/SSL加密协同工作,其认证机制仍然是可以接受的。

       Digest: 如RFC2617所定义的Digest认证机制,Digest认证机制明显比Basic要更安全,该机制对于那些顾虑TLS/SSL加密所带来的开销的应用会是一个比较好的选择。

       NTLM: NTLM是一个微软开发的并且针对Windows平台进行了优化的专有认证机制,NTML被认为比Digest更安全。

       SPNEGO: SPNEGO(Simple and Protected GSSAPI Negotiation Mechanism)是一个GSSAPI伪机制,用于协商那些潜在的真正机制,SPNEGO最明显的用途是在微软的HTTP Negotiate验证扩展,可协商的子机制包含活动目录中支持的NTML和Kerberos,现在HttpClient只支持Kerberos子机制。

       Kerberos: Kerberos认证的实现。

4.3 凭证提供器

       凭证提供器旨在维护一组用户凭证并且能够为特定的认证域生产用户凭证,认证领域包含一个主机名,一个端口名,一个域名称和一个认证机制名,当使用凭证提供器(能够提供任意的主机,任意端口,任意域名和任意机制)来替代具体属性值注册凭证时,其期望能够为某个特定认证域找到一个最贴近的匹配值(在直接匹配找不到的情况下)。

       HttpClient可以与任意实现了CredentialsProvider接口的实际凭证提供器协同工作,默认的CredentialsProvider实现叫做BasicCredentialsProvider,其是一个依赖于java.util.HashMap的简单实现。

//凭证注册器
CredentialsProvider credsProvider = new BasicCredentialsProvider();
//注册1 somehost主机名  任意PORT  指定用户U1 密码P1
credsProvider.setCredentials(
        new AuthScope("somehost", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("u1", "p1"));
//注册2 somehost主机名  8080端口  用户U2 密码P2
credsProvider.setCredentials(
        new AuthScope("somehost", 8080),
new UsernamePasswordCredentials("u2", "p2"));
//注册3  otherhost主机名   8080端口   任意域   认证机制ntlm   用户U3  密码P3 
credsProvider.setCredentials(
        new AuthScope("otherhost", 8080, AuthScope.ANY_REALM, "ntlm"),
new UsernamePasswordCredentials("u3", "p3"));

//与somehost 端口80  realm域  basic认证机制匹配的凭证
System.out.println(credsProvider.getCredentials(
        new AuthScope("somehost", 80, "realm", "basic")));
//与somehost 端口8080  realm域  basic认证机制匹配的凭证
System.out.println(credsProvider.getCredentials(
        new AuthScope("somehost", 8080, "realm", "basic")));
//与otherhost 端口8080  realm域  basic认证机制匹配的凭证
System.out.println(credsProvider.getCredentials(
        new AuthScope("otherhost", 8080, "realm", "basic")));
//与otherhost 端口8080  realm域  basic认证机制匹配的凭证
System.out.println(credsProvider.getCredentials(
        new AuthScope("otherhost", 8080, null, "ntlm")));

输出

//与注册1最类似
[principal: u1]
//与注册2最类似
[principal: u2]
//都不类似 因为没有对应的主机名和认证机制
null
//与注册3最类似
[principal: u3]

4.4 HTTP身份认证和执行环境

       HttpClient依靠AuthState类来对认证处理状态的详细信息进行跟踪,HttpClient在HTTP请求执行过程中创建了两个AuthState实例:一个用于目标主机认证另一个用于代理认证,由于目标服务器或者代理服务器都可能要求用户身份认证,所以各自的AuthScope实例将会被认证过程中所使用的AuthScope,AuthScheme和Credentials所填充,AuthState可以用于检查请求是属于何种认证类型,无论是否找到匹配的AuthScheme实现,也不管是否有凭证提供器用于查找特定的用户凭证。

在HTTP请求执行的过程中,HttpClient添加如下的认证相关对象到执行环境中:

       Lookup实例代表实际注册的认证机制,该属性值优先级为局部环境中的值大于默认值。

       CredentialsProvider实例代表了实际的凭证提供器,该属性值优先级为局部环境中的值大于默认值。

       AuthState实例代表了实际的目标认证状态,该属性值优先级为局部环境中的值大于默认值。

       AuthState实例代表了实际的代理认证状态,该属性值优先级为局部环境中的值大于默认值。

       AuthCache实例代表了实际的认证数据缓存,该属性值优先级为局部环境中的值大于默认值。

       局部HttpContext对象可以先于request执行环来定制化Http认证环境,或者在请求执行之后用于检查其状态。

CloseableHttpClient httpclient = <...>

CredentialsProvider credsProvider = <...>
Lookup<AuthSchemeProvider> authRegistry = <...>
AuthCache authCache = <...>
//HttpClient上下文
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthSchemeRegistry(authRegistry);
context.setAuthCache(authCache);
HttpGet httpget = new HttpGet("http://somehost/");
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会自动缓存其认证成功的主机的相关信息,请注意你必须使用相同的执行环境去执行逻辑相关的请求,以此保证缓存的认证数据能够在不同的request之间传输,当执行环境超出范围时认证数据会立即被丢弃。

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 = new BasicAuthCache();
// 创建基础认证机制 添加到缓存
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);

// 将认证缓存添加到执行环境中  即预填充
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);

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对NTMLv1,NTLMv2,和NTML2绘画认证提供了现成的支持,你可以继续使用外部的NTLM引擎比如JCIFS库(Samba工程作为他们的windows协同编程套件研发)。

4.7.1 NTLM连接持久化

       NTLM认证机制在计算开销和性能影响上明显比Basic和Digest机制更昂贵,这很可能是微软选择将NTLM认证机制做成有状态化的主要原因之一,换句话说,一旦已经认证过了,用户的身份就跟整个生命周期的连接关联起来了,NTML连接这种有状态的性质使得连接持久化更加复杂,一个明显的情形是持久化的NTLM连接不会被不同身份的另一个用户重用。而HttpClient附带的标准连接管理器完全有能力管理有状态的连接,但是,有一点非常重要的就是逻辑相关request要使用同一个执行上下文和会话,以便于他们共享同一个当前用户。否则,对于每个请求NTML保护资源的request,HttpClient最终会为其创建一个新的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");

//确保相同逻辑的request使用同一个context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);

//使用GET来触发NTLM认证
HttpGet httpget = new HttpGet("/ntlm-protected/info");
CloseableHttpResponse response1 = httpclient.execute(target, httpget, context);
try {
    HttpEntity entity1 = response1.getEntity();
} finally {
    response1.close();
}

//使用POST来提交请求
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 Negotiation Mechanism) 设计成在不知道服务可以提供或者使用什么时为其提供认证,其最普遍的用法是扩展Kerberos认证,它可以包装其他机制,但是在HttpClient中,SPNEGO的当前版本唯一关心的只有Kerberos。

4.8.1 HttpClient中的SPNEGO支持

       SPNEGO认证机制能够工作在Sun Java的1.5版本及以上,但是建议使用1.6及以上的版本,对SPNEGO认证的支持会更加完整一些。

       SUN JRE提供了支持类几乎能够处理所有的Kerberos和SPNEGO的token,这意味着为了使用GSS类你需要做很多的设置,SPNegoScheme是一个简单的用于处理token编组和正确读写headers的类。

       开始的最好方式是用KerberosHttpClient.java类,尝试编写一个示例并跑起来,中途可能会发生很多异常情况,但是幸运的话还是能够正常工作的,为了调试你应该打印出足够多的调试信息。

       在Windows里面,它应该默认使用已存在的登录凭证,该方式可以通过使用'kinit'重写,如$JAVA_HOME\bin\kinit testuser@AD.EXAMPLE.NET ,kinit对于调试异常和进行测试非常有用,你可以通过删除kinit的缓存文件来回滚到windows的Kerberos。

       确信在krb5.conf文件中列出了domain_realms,这通常是发生问题的主要原因之一。

4.8.2 GSS/Java Kerberos配置

       该文档假设你使用windows,但是许多信息在Unix会更加适用。

       org.ietf.jgss类有许多的配置参数,主要都在krb5.conf/krb5.ini文件,更多的信息可以查看http://web.mit.edu/kerberos/krb5-1.4/krb5-1.4.1/doc/krb5-admin/krb5.conf.html

4.8.3 log.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文件

       如果没有指定的话,系统默认使用该文件,你可以设置krb5.conf文件中的java.security.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使用当前用户的身份票据,必须设置系统属性javax.security.auth.useSubjectCredsOnly为false并且Windows注册表项"allowtgtsessionkey"应该被正确的添加和设置,以允许session的key能够被发送到Kerberos Ticket-Granting Tikect(Kerberos授权票据)

       在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







目录
相关文章
|
2月前
|
缓存 网络协议 安全
49. 【Android教程】HTTP 使用详解
49. 【Android教程】HTTP 使用详解
39 1
|
1月前
|
JSON 网络协议 安全
《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(1)-HTTP和HTTPS基础知识
【7月更文挑战第16天】本文介绍了HTTP和HTTPS协议的基本概念与作用,强调了理解HTTP协议对使用抓包工具Fiddler的重要性。HTTP是用于Web浏览器与服务器间信息传输的协议,不加密,易被截取,不适合传输敏感信息。HTTPS是HTTP的安全版,通过SSL/TLS提供加密和服务器身份验证,确保数据安全。HTTP请求包括请求行、请求头、空行和可选的请求主体,响应则有响应行、响应头、空行和响应主体。HTTP协议无状态,而HTTPS解决了安全性问题,但也带来了额外的计算开销。Fiddler作为一个强大的抓包工具,可以帮助开发者和测试人员分析HTTP/HTTPS通信,理解请求和响应的结构。
33 4
《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(1)-HTTP和HTTPS基础知识
|
30天前
|
Web App开发 XML 缓存
《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(4)-会话面板和HTTP会话数据操作详解
【7月更文挑战第19天】Fiddler会话面板概览:Fiddler的会话列表显示HTTP请求的详细信息,包括ID、状态码、协议、主机名、URL、内容类型、大小、进程及自定义备注。颜色和图标标识状态,如红色表示错误,黄色为认证,蓝色是HTML响应。用户可右键列进行搜索、标记重复、隐藏或自定义列,如添加请求方法。通过界面或脚本可添加自定义列,如显示ServerIP。会话还可复制和保存,解决乱码问题需解码响应体。
45 0
《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(4)-会话面板和HTTP会话数据操作详解
|
1月前
|
网络协议 程序员 应用服务中间件
Swoole与Go系列教程之HTTP服务的应用
PHP 曾是Web开发领域佼佼者,随着业务壮大,异步和高并发方面不足显现。Swoole 曾经尝试填补空白,但局限性也比较的明显。Go 语言的崛起,简洁语法和并发优势吸引大厂使用,吸引了大多数程序员的转型。
988 0
Swoole与Go系列教程之HTTP服务的应用
|
2月前
|
Java
JSP 教程 之 JSP HTTP 状态码 4
JSP教程讲解了HTTP状态码的使用,包括HTTP响应的结构和设置状态码的方法:通过HttpServletResponse的setStatus、sendRedirect及sendError。示例展示了如何发送407错误码,浏览器显示&quot;Need authentication!!!&quot;。
24 1
|
2月前
|
Java 数据安全/隐私保护
JSP 教程 之 JSP HTTP 状态码 2
JSP教程讲解了HTTP状态码,包括成功、重定向、客户端错误和服务器错误等类别。例如,200表示请求成功,404表示页面未找到,500表示服务器内部错误。这些状态码帮助理解HTTP通信过程中发生的问题。
32 2
|
3月前
|
Web App开发 缓存 前端开发
《手把手教你》系列技巧篇(四十四)-java+ selenium自动化测试-处理https 安全问题或者非信任站点-下篇(详解教程)
【5月更文挑战第8天】这篇文档介绍了如何在IE、Chrome和Firefox浏览器中处理不信任证书的问题。作者北京-宏哥分享了如何通过编程方式跳过浏览器的证书警告,直接访问不受信任的HTTPS网站。文章分为几个部分,首先简要介绍了问题背景,然后详细讲解了在Chrome浏览器中的两种方法,包括代码设计和运行效果,并给出了其他浏览器的相关信息和参考资料。最后,作者总结了处理此类问题的一些通用技巧。
81 2
|
3月前
|
Web App开发 JavaScript 前端开发
《手把手教你》系列技巧篇(四十三)-java+ selenium自动化测试-处理https 安全问题或者非信任站点-上篇(详解教程)
【5月更文挑战第7天】本文介绍了如何在Java+Selenium自动化测试中处理浏览器对不信任证书的处理方法,特别是针对IE、Chrome和Firefox浏览器。在某些情况下,访问HTTPS网站时会遇到证书不可信的警告,但可以通过编程方式跳过这些警告。
54 1
|
3月前
|
缓存 安全 应用服务中间件
蓝易云 - Nginx的HTTPS部署与安全性能优化教程
以上就是在Nginx上部署HTTPS并进行安全性能优化的基本步骤。需要注意的是,这些步骤可能会根据您的具体需求和环境有所不同。
41 0
|
3月前
|
Java
Servlet 教程 之 Servlet HTTP 状态码 3
该Servlet教程聚焦于HTTP状态码,示例展示如何向客户端发送407错误,提示&quot;Need authentication!!!&quot;. 类名为`showError`的Servlet扩展自`HttpServlet`,重写`doGet`和`doPost`方法。当遇到GET或POST请求时,它会设置HTTP状态码为407并附带错误信息。
23 2