第四章 HTTP认证java
任何用户身份验证的过程都须要一组能够用于创建用户身份的凭据。用户凭证的最简单的形式能够仅仅是用户名/密码对。UsernamePasswordCredentials表明了一组包含安全规则和明文密码的凭据。这个实现对由HTTP标准规范中定义的标准认证模式是足够的安全
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");System.out.println(creds.getUserPrincipal().getName());System.out.println(creds.getPassword());
输出内容为:服务器
userpwd
NTCredentials是微软Windows指定的实现,它包含了除了用户名/密码对外,一组额外的Windows指定的属性,好比用户域名的名字,好比在微软的Windows网络中,相同的用户使用不一样设置的认证能够属于不一样的域。网络
NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");System.out.println(creds.getUserPrincipal().getName());System.out.println(creds.getPassword());
输出内容为:session
DOMAIN/userpwd
凭据提供器意来维护一组用户凭据,还有可以对特定认证范围生产用户凭据。认证范围包括主机名,端口号,领域名称和认证模式名称。当使用凭据提供器来注册凭据时,咱们能够提供一个通配符(任意主机,任意端口,任意领域,任意模式)来替代肯定的属性值。若是直接匹配没有发现,凭据提供器指望被用来发现最匹配的特定范围。框架
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")));
输出内容为:dom
[principal: u1][principal: u2]null[principal: u3]
HttpClient依赖于AuthState类来跟踪关于认证过程状态的详细信息。在HTTP请求执行过程当中,HttpClient建立2个AuthState的实例:一个对于目标主机认证,另一个对于代理认证。若是目标服务器或代理须要用户认证,那么各自的AuthState实例将会被在认证处理过程当中使用的AuthScope,AuthScheme和Crednetials来填充。AuthState能够被检查来找出请求的认证是什么类型的,是否匹配AuthScheme的实现,是否凭据提供器对给定的认证范围去找用户凭据。ide
在HTTP请求执行的过程当中,HttpClient添加了下列和认证相关的对象到执行上下文中:post
本地的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());
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);// 若是发现了,抢先生成BasicSchemeif (creds != null) {authState.setAuthScheme(new BasicScheme());authState.setCredentials(creds);}}}};DefaultHttpClient httpclient = new DefaultHttpClient();// 做为第一个拦截器加入到协议链中httpclient.addRequestInterceptor(preemptiveAuth, 0);
当前HttpClient没有提对开箱的NTLM认证模式的支持也可能永远也不会。这个缘由是法律上的而不是技术上的。然而,NTLM认证可使用外部的NTLM引擎好比JCIFS[http://jcifs.samba.org/]来开启,类库由Samba[http://www.samba.org/]项目开发,做为它们Windows的交互操做程序套装的一部分。要获取详细内容请参考HttpClient发行包中包含的NTLM_SUPPORT.txt文档。
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();}