HttpClient既支持HTTP标准规范定义的认证模式,又支持一些广泛使用的非标准认证模式,比如NTLM和SPNEGO。
任何用户认证的过程,都需要一系列的凭证来确定用户的身份。最简单的用户凭证可以是用户名和密码这种形式。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
AutoScheme
接口表示一个抽象的面向挑战/响应的认证方案。一个认证方案要支持下面的功能:
请注意:一个认证方案可能是有状态的,因为它可能涉及到一系列的挑战/响应。
HttpClient实现了下面几种AutoScheme
:
凭证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]
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("http://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.1开始,HttpClient就会自动缓存验证通过的认证信息。但是为了使用这个缓存的认证信息,我们必须在同一个上下文中执行逻辑相关的请求。一旦超出该上下文的作用范围,缓存的认证信息就会失效。
HttpClient默认不支持抢先认证,因为一旦抢先认证被误用或者错用,会导致一系列的安全问题,比如会把用户的认证信息以明文的方式发送给未授权的第三方服务器。因此,需要用户自己根据自己应用的具体环境来评估抢先认证带来的好处和带来的风险。
即使如此,HttpClient还是允许我们通过配置来启用抢先认证,方法是提前填充认证信息缓存到上下文中,这样,以这个上下文执行的方法,就会使用抢先认证。