HttpClient4.3教程 第四章 HTTP认证

HttpClient4.3教程 第四章 HTTP认证

Posted on 2013 年 10 月 17 日

HttpClient既支持HTTP标准规范定义的认证模式,又支持一些广泛使用的非标准认证模式,比如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系统使用的一种凭据,包含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

4.2. 认证方案

AutoScheme接口表示一个抽象的面向挑战/响应的认证方案。一个认证方案要支持下面的功能:

  • 客户端请求服务器受保护的资源,服务器会发送过来一个chanllenge(挑战),认证方案(Authentication scheme)需要解析、处理这个挑战
  • 为processed challenge提供一些属性值:认证方案的类型,和此方案需要的一些参数,这种方案适用的范围
  • 使用给定的授权信息生成授权字符串;生成http请求,用来响应服务器发送来过的授权challenge

请注意:一个认证方案可能是有状态的,因为它可能涉及到一系列的挑战/响应。

HttpClient实现了下面几种AutoScheme:

  • Basic: Basic认证方案是在RFC2617号文档中定义的。这种授权方案用明文来传输凭证信息,所以它是不安全的。虽然Basic认证方案本身是不安全的,但是它一旦和TLS/SSL加密技术结合起来使用,就完全足够了。
  • Digest: Digest(摘要)认证方案是在RFC2617号文档中定义的。Digest认证方案比Basic方案安全多了,对于那些受不了Basic+TLS/SSL传输开销的系统,digest方案是个不错的选择。
  • NTLM: NTLM认证方案是个专有的认证方案,由微软开发,并且针对windows平台做了优化。NTLM被认为比Digest更安全。
  • SPNEGO: SPNEGO(Simple and Protected GSSAPI Negotiation Mechanism)是GSSAPI的一个“伪机制”,它用来协商真正的认证机制。SPNEGO最明显的用途是在微软的HTTP协商认证机制拓展上。可协商的子机制包括NTLM、Kerberos。目前,HttpCLient只支持Kerberos机制。(原文:The negotiable sub-mechanisms include NTLM and Kerberos supported by Active Directory. At present HttpClient only supports the Kerberos sub-mechanism.)

4.3. 凭证 provider

凭证providers旨在维护一套用户的凭证,当需要某种特定的凭证时,providers就应该能产生这种凭证。认证的具体内容包括主机名、端口号、realm name和认证方案名。当使用凭据provider的时候,我们可以很模糊的指定主机名、端口号、realm和认证方案,不用写的很精确。因为,凭据provider会根据我们指定的内容,筛选出一个最匹配的方案。

只要我们自定义的凭据provider实现了CredentialsProvider这个接口,就可以在HttpClient中使用。默认的凭据provider叫做BasicCredentialsProvider,它使用java.util.HashMapCredentialsProvider进行了简单的实现。

    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.4.HTTP授权和执行上下文

HttpClient依赖AuthState类去跟踪认证过程中的状态的详细信息。在Http请求过程中,HttpClient创建两个AuthState实例:一个用于目标服务器认证,一个用于代理服务器认证。如果服务器或者代理服务器需要用户的授权信息,AuthScopeAutoScheme和认证信息就会被填充到两个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.5. 缓存认证数据

从版本4.1开始,HttpClient就会自动缓存验证通过的认证信息。但是为了使用这个缓存的认证信息,我们必须在同一个上下文中执行逻辑相关的请求。一旦超出该上下文的作用范围,缓存的认证信息就会失效。

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 authCache = new BasicAuthCache();
    //创建 BasicScheme,并把它添加到 auth cache中
    BasicScheme basicAuth = new BasicScheme();
    authCache.put(targetHost, basicAuth);

    // 把AutoCache添加到上下文中
    HttpClientContext context = HttpClientContext.create();
    context.setCredentialsProvider(credsProvider);

    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就全面支持NTLMv1、NTLMv2和NTLM2认证。当人我们可以仍旧使用外部的NTLM引擎(比如Samba开发的JCIFS库)作为与Windows互操作性程序的一部分。

4.7.1. NTLM连接持久性

相比BasicDigest认证,NTLM认证要明显需要更多的计算开销,性能影响也比较大。这也可能是微软把NTLM协议设计成有状态连接的主要原因之一。也就是说,NTLM连接一旦建立,用户的身份就会在其整个生命周期和它相关联。NTLM连接的状态性使得连接持久性更加复杂,The stateful nature of NTLM connections makes connection persistence more complex, as for the obvious reason persistent NTLM connections may not be re-used by users with a different user identity. HttpClient中标准的连接管理器就可以管理有状态的连接。但是,同一会话中逻辑相关的请求,必须使用相同的执行上下文,这样才能使用用户的身份信息。否则,HttpClient就会结束旧的连接,为了获取被NTLM协议保护的资源,而为每个HTTP请求,创建一个新的Http连接。更新关于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");

    //使用相同的上下文来执行逻辑相关的请求
    HttpClientContext context = HttpClientContext.create();
    context.setCredentialsProvider(credsProvider);

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

    //使用相同的上下文,执行重量级的方法
    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 Megotiation Mechanism),当双方均不知道对方能使用/提供什么协议的情况下,可以使用SP认证协议。这种协议在Kerberos认证方案中经常使用。It can wrap other mechanisms, however the current version in HttpClient is designed solely with Kerberos in mind.

4.8.1. 在HTTPCIENT中使用SPNEGO

SPNEGO认证方案兼容Sun java 1.5及以上版本。但是强烈推荐jdk1.6以上。Sun的JRE提供的类就已经几乎完全可以处理Kerberos和SPNEGO token。这就意味着,需要设置很多的GSS类。SpnegoScheme是个很简单的类,可以用它来handle marshalling the tokens and 读写正确的头消息。

最好的开始方法就是从示例程序中找到KerberosHttpClient.java这个文件,尝试让它运行起来。运行过程有可能会出现很多问题,但是如果人品比较高可能会顺利一点。这个文件会提供一些输出,来帮我们调试。

在Windows系统中,应该默认使用用户的登陆凭据;当然我们也可以使用kinit来覆盖这个凭据,比如$JAVA_HOME\bin\kinit [email protected],这在我们测试和调试的时候就显得很有用了。如果想用回Windows默认的登陆凭据,删除kinit创建的缓存文件即可。

确保在krb5.conf文件中列出domain_realms。这能解决很多不必要的问题。

4.8.2. 使用GSS/JAVA KERBEROS

下面的这份文档是针对Windows系统的,但是很多信息同样适合Unix。

org.ietf.jgss这个类有很多的配置参数,这些参数大部分都在krb5.conf/krb5.ini文件中配置。更多的信息,参考此处

login.conf文件

下面是一个基本的login.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 文件

如果没有手动指定,系统会使用默认配置。如果要手动指定,可以在java.security.krb5.conf中设置系统变量,指定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使用当前用户的tickets,javax.security.auth.useSubjectCredsOnly这个系统变量应该设置成false,并且需要在Windows注册表中添加allowtgtsessionkey这个项,而且要allow session keys to be sent in the Kerberos Ticket-Granting Ticket.

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

易踪网:每天进步一点点

转载请保留链接地址: http://www.yeetrack.com/?p=825

时间: 2024-11-10 04:59:18

HttpClient4.3教程 第四章 HTTP认证的相关文章

HttpClient4.3 教程 第五章 快速API

HttpClient4.3 教程 第五章 快速API Posted on 2013 年 10 月 22 日 5.1.Easy to use facade API HttpClient从4.2开始支持快速api.快速api仅仅实现了HttpClient的基本功能,它只要用于一些不需要灵活性的简单场景.例如,快速api不需要用户处理连接管理和资源释放. 下面是几个使用快速api的例子: // 执行一个get方法,设置超时时间,并且将结果变成字符串 Request.Get("http://www.ye

[Learn Android Studio 汉化教程]第四章 : Refactoring Code

[Learn Android Studio 汉化教程]第四章 : Refactoring Code 第四章 Refactoring Code    重构代码 在Android Studio中开发,解决方案不会总是一蹴而成的.作为一个有效率的编程者,在你的开发,调试和测试中需要一些弹性以及代码重构.随着在这章中的行进,你将明白Android Studio如何产生代码:在这章里你将看到Android Studio如何重构你的代码.重构代码最大的风险是可能引入不期望的错误.通过分析某些风险重构操作的结

(转)JAVA AJAX教程第四章—AJAX和MVC的简单结合

这里我们再理解了AJAX后,开始来用实例感受AJAX的力量. 今天我最后要实现的效果,当鼠标放到图片上时会根据,会把数据库库里的数据读出,通过显示框显示出来.这个在很多网上商店都有用到这里效果,我们这里用AJAX来实现这个效果.这个实例里结合了MySql.Servlet还有Js,理论性很少,但通过实践来感受理论知识. 一个页面中应用了 AJAX,同时他也能实现MVC框架,可以吗?当然可以的.通过页面调用AJAX,这个页面就是MVC中的V,他用来前台显示与用户交互的内容.用 JavaBean作为M

HttpClient4.3教程 第三章 Http状态管理

HttpClient4.3教程 第三章 Http状态管理 Posted on 2013 年 10 月 13 日 最初,Http被设计成一个无状态的,面向请求/响应的协议,所以它不能在逻辑相关的http请求/响应中保持状态会话.由于越来越多的系统使用http协议,其中包括http从来没有想支持的系统,比如电子商务系统.因此,http支持状态管理就很必要了. 当时的web客户端和服务器软件领先者,网景(netscape)公司,最先在他们的产品中支持http状态管理,并且制定了一些专有规范.后来,网景

2018-06-20 中文代码示例视频演示Python入门教程第四章 控制流

知乎原链 续前作: 中文代码示例视频演示Python入门教程第三章 简介Python 对应在线文档: 4. More Control Flow Tools 录制中出了不少岔子. 另外, 输入法确实是一个短板. 中文代码示例Python入门教程 4.1 if条件语句 & 4.2 for语句_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili? 中文代码示例Python入门教程 4.3 range函数_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili? 中文代码示例Python入门教程 4.4 b

Flask 教程 第四章:数据库

本文翻译自 The Flask Mega-Tutorial Part IV: Database 在Flask Mega-Tutorial系列的第四部分,我将告诉你如何使用数据库. 本章的主题是重中之重!大多数应用都需要持久化存储数据,并高效地执行的增删查改的操作,数据库为此而生. 本章的GitHub链接为: Browse, Zip, Diff. Flask中的数据库 Flask本身不支持数据库,相信你已经听说过了. 正如表单那样,这也是Flask有意为之.对使用的数据库插件自由选择,岂不是比被迫

storm入门教程 第四章 消息的可靠处理【转】

4.1 简介 storm可以确保spout发送出来的每个消息都会被完整的处理.本章将会描述storm体系是如何达到这个目标的,并将会详述开发者应该如何使用storm的这些机制来实现数据的可靠处理. 4.2 理解消息被完整处理 一个消息(tuple)从spout发送出来,可能会导致成百上千的消息基于此消息被创建. 我们来思考一下流式的“单词统计”的例子: storm任务从数据源(Kestrel queue)每次读取一个完整的英文句子:将这个句子分解为独立的单词,最后,实时的输出每个单词以及它出现过

SQLAlchemy教程-第四章-SQL常用查询的ORM写法进阶2-待完善

内容预告 别名的用法 函数的用法 分组的写法. case when的用法. exists的用法 为啥要用别名? 如果两个表的两列名字相同, 那么为了区分这两列, 必须使用别名. 如果有一列是通过运算出来的, 那么一定要给这列起个名字, 否则无法在查询结果中引用. 看别名的写法. 先把代码贴上来, 然后注释我都在代码里写了, 请大家自行阅读~ (#^.^#) from models import * # 别名 def query_with_column_alias(): emps = sess.q

java2 实用教程第四章

博主原创 转载请注明地址 博客:http://www.cnblogs.com/13224ACMer/ 1成员变量 声明变量所声明的变量被称为成员变量和域变量,成员变量在类中的书写位置与前后顺序无关, 但是有个特点就是变量的赋值问题:成员变量在定义的时候可以有默认值,而域变量在定义的时候没默认值,所以在定义的时候应该特别注意是否需要赋值的问题 变量名的定义也有规范的要求,如果变量是一个单词组成的,那么最好首字母大写,如果是两个甚至两个以上的单词组成,那么最好从第二个单词开始首字母大写 在类中对变量