HTTP认证

HTTP认证解决的核心问题是:你是谁?

http提供了一系列的技术和机器,可用来跟踪身份,进行安全性检测,控制对内容的访问。

一、常见的HTTP认证方式

  • Basic认证
  • Digest认证
  • SSL Client认证
  • 表单认证

1. Basic认证

为了使web站点的登录更加简便,HTTP中包含了一种内建机制,可以用WWW-Authenticate首部和Authorization首部向web站点传送用户的相关信息。一旦登录,浏览器就可以不断地在每条发往这个站点的请求中发送这个登录信息了。在HTTP中,基本认证是一种用来允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式。在发送之前是以用户名追加一个冒号然后串接上口令,并将得出的结果字符串再用Base64算法编码。例如,提供的用户名是Aladdin、口令是open sesame,则拼接后的结果就是Aladdin:open sesame,然后再将其用Base64编码,得到QWxhZGRpbjpvcGVuIHNlc2FtZQ==。最终将Base64编码的字符串发送出去,由接收者解码得到一个由冒号分隔的用户名和口令的字符串。虽然对用户名和口令的Base64算法编码结果很难用肉眼识别解码,但它仍可以极为轻松地被计算机所解码,就像其容易编码一样。编码这一步骤的目的并不是安全与隐私,而是为将用户名和口令中的不兼容的字符转换为均与HTTP协议兼容的字符集。

一个典型的HTTP客户端和HTTP服务器的对话,服务器安装在同一台计算机上(localhost),包含以下步骤:

  • 客户端请求一个需要身份认证的页面,但是没有提供用户名和口令。这通常是用户在地址栏输入一个URL,或是打开了一个指向该页面的链接。
  • 服务端响应一个401应答码,并提供一个认证域。
  • 接到应答后,客户端显示该认证域(通常是所访问的计算机或系统的描述)给用户并提示输入用户名和口令。此时用户可以选择确定或取消。
  • 用户输入了用户名和口令后,客户端软件会在原先的请求上增加认证消息头(值是base64encode(username+":"+password)),然后重新发送再次尝试。
  • 在本例中,服务器接受了该认证屏幕并返回了页面。如果用户凭据非法或无效,服务器可能再次返回401应答码,客户端可以再次提示用户输入口令。

注意:客户端有可能不需要用户交互,在第一次请求中就发送认证消息头。

优点:基本上所有流行的网页浏览器都支持基本认证,在可信网络环境中使用基本认证。

缺点:保密性不强,不能跨站点,不同的站点需要重新输入账户密码;现存的浏览器保存认证信息直到标签页或浏览器被关闭,或者用户清除历史记录。HTTP没有为服务器提供一种方法指示客户端丢弃这些被缓存的密钥。这意味着服务器端在用户不关闭浏览器的情况下,并没有一种有效的方法来让用户登出。

2. Digest认证

认证步骤:

  • 客户端访问Http资源服务器。由于需要Digest认证,服务器返回了两个重要字段nonce(随机数)和realm。
  • 客户端构造Authorization请求头,值包含username、realm、nouce、uri和response的字段信息。其中,realm和nouce就是第一步返回的值。nouce只能被服务端使用一次。uri(digest-uri)即Request-URI的值,但考虑到经代理转发后Request-URI的值可能被修改、因此实现会复制一份副本保存在uri内。response也可叫做Request-digest,存放经过MD5运算后的密码字符串,形成响应码。
  • 服务器验证包含Authorization值的请求,若验证通过则可访问资源。

Digest认证可以防止密码泄露和请求重放,但没办法防假冒。所以安全级别较低。

Digest和Basic认证一样,每次都会发送Authorization请求头,也就相当于重新构造此值。所以两者易用性都较差。

3. SSL认证

SSL认证安全级别较高,但需要承担证书费用,涉及到一些重要的概念:数字证书机构的公钥、证书的私钥和公钥、非对称算法(配合证书的私钥和公钥使用)、对称密钥、对称算法(配合对称密钥使用)。

认证步骤如下:

  • 客户端请求服务资源,服务器要求客户端出示数字证书。
  • 客户端发送数字证书。
  • 服务器通过数字证书机构的公钥验证数字证书的合法性,验证通过后取出证书的公钥。
  • 服务端随机生成一个随机数即为对称密钥,并使用非对称算法和证书公钥加密。这个加密后的字符串,只有发送的客户端能解。
  • 服端使用非对称解密算法和证书私钥获取服务端发送的对称密钥。后续客户端和服务端的请求直接基于对称算法和对称密钥。由于只有客户端和服务端有对称密钥,所以后续发送的请求较安全。

SSL可以防泄漏、假冒、重放,所以在Web系统中得到了广泛的应用。

SSL客户端认证在实际中用得不多,因为一来需要在客户端中安装证书(升级麻烦)、二来需要承担证书费用。

4. Form表单认证

基于表单的认证方式并不存在于HTTP规范。所以实现方式也呈现多样化。表单认证一般都会配合cookie+sessiond的使用,现在绝大多数的Web站点都是使用此认证方式。用户在登录页中填写用户名和密码,服务端认证通过后会将sessionId返回给浏览器端,浏览器会保存sessionId到浏览器的Cookie中。因为Http是无状态的,所以浏览器使用Cookie来保存sessionId。下次客户端发送的请求中会包含sessionId值,服务端发现sessionId存在并认证过则会提供资源访问。

二、HttpClient的认证

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

1. 用户凭证

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

HttpClientBuilder httpClientBuilder = HttpClients.custom();
//凭据提供器, 需要指定认证范围和认证凭据
CredentialsProvider credsProvider = new BasicCredentialsProvider();
//认证凭据
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("username","password");
//认证范围
AuthScope authScope = new AuthScope("192.168.12.1", 8888);
credsProvider.setCredentials(authScope, creds);
httpClientBuilder.setDefaultCredentialsProvider(credsProvider);

CloseableHttpClient httpClient = httpClientBuilder.build();

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

//wordstation指发出请求的机器名称,一般是电脑名,domain是域的名称
NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");

2. 认证机制

AuthScheme接口表示一个抽象的面向挑战-应答的认证机制。一个认证机制一般会期望有如下的功能:

  • 解析和处理目标服务器发送的挑战并且对受保护资源的请求做出应答。
  • 提供处理后挑战的属性:认证机制类型和其参数,比如认证机制适用的范围(可能的话)
  • 为所给的凭证和HTTP request生成认证字符串用于认证挑战的应答。

HttpClient自带了多个AuthScheme的实现:

  • BasicScheme:如RFC2617所定义的基本认证机制,由于凭证通过明文传递,该认证机制是不安全的,尽管其安全性比较低,但是如果与TLS/SSL加密协同工作,其认证机制仍然是可以接受的。
  • DigestScheme:如RFC2617所定义的Digest认证机制,Digest认证机制明显比Basic要更安全,该机制对于那些顾虑TLS/SSL加密所带来的开销的应用会是一个比较好的选择。
  • SPNegoScheme:SPNEGO(Simple and Protected GSSAPI Negotiation Mechanism)是一个GSSAPI伪机制,用于协商那些潜在的真正机制,SPNEGO最明显的用途是在微软的HTTP Negotiate验证扩展,可协商的子机制包含活动目录中支持的NTML和Kerberos,现在HttpClient只支持Kerberos子机制。
  • NTLMScheme:NTLM是一个微软开发的并且针对Windows平台进行了优化的专有认证机制,NTML被认为比Digest更安全。
  • KerberosScheme:Kerberos认证的实现。

3. 凭证提供器

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

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

4. HTTP身份认证和执行环境

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

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

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

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

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

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

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

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

//执行上下文
HttpClientContext context = HttpClientContext.create();

CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("192.168.1.1", 8080),
        new UsernamePasswordCredentials("username", "password"));
context.setCredentialsProvider(credsProvider);

Lookup<AuthSchemeProvider> authSchemeRegistry =
        RegistryBuilder.<AuthSchemeProvider> create()
            .register(AuthSchemes.BASIC, new BasicSchemeFactory())
            .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
            .register(AuthSchemes.NTLM, new NTLMSchemeFactory())
            .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
            .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
            .build();
context.setAuthSchemeRegistry(authSchemeRegistry);

//创建 AuthCache 对象
AuthCache authCache = new BasicAuthCache();
//创建 BasicScheme,并把它添加到 auth cache中
BasicScheme basicAuth = new BasicScheme();
HttpHost host = new HttpHost("192.168.1.1", 8080);
authCache.put(host, basicAuth);
//把AutoCache添加到上下文中
context.setAuthCache(authCache);

5. 认证数据缓存

自从4.1版本开始,HttpClient会自动缓存其认证成功的主机的相关信息,请注意你必须使用相同的执行环境去执行逻辑相关的请求,以此保证缓存的认证数据能够在不同的request之间传输,当执行环境超出范围时认证数据会立即被丢弃。

6. 抢占式认证

HttpClient没有现成的方式支持抢占式认证,因为如果抢占认证被误用或者使用不当会导致重大安全问题,比如明文发送用户凭证到非授权的第三方,所以,希望用户能够在其具体的应用环境中评估抢占式认证的好处和其潜在的风险。

尽管如此,你可以通过预填充认证数据缓存的方式来配置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);
原文地址:https://www.cnblogs.com/myitnews/p/12202095.html