HTTP Digest authentication

什么是摘要认证

摘要认证( Digest authentication)是一个简单的认证机制,最初是为HTTP协议开发的,因而也常叫做HTTP摘要,在RFC2617中描述。其身份验证机制很简单,它采用杂凑式(hash)加密方法,以避免用明文传输用户的口令。

摘要认证就是要核实,参与通信的双方,都知道双方共享的一个秘密(即口令)。

摘要认证流程

图一

  • 服务器核实用户身份

    server收到client的HTTP request(INVITE),如果server需要客户端摘要认证,就需要生成一个摘要盘问(digest challenge),通过Response给client一个401 Unauthorized状态发送给用户。

    摘要盘问如 图二 中的WWW-Authenticate header所示:

    图二

    摘要盘问中的各个参数意义如下:

  • realm(领域):必须的,在所有的盘问中都必须有。它是目的是鉴别SIP消息中的机密。在实际应用中,它通常设置为server所负责的域名。

  • nonce (现时):必须的,这是由服务器规定的数据字符串,在服务器每次产生一个摘要盘问时,这个参数都是不一样的(与前面所产生的不会雷同)。nonce 通常是由一些数据通过md5杂凑运算构造的。这样的数据通常包括时间标识和服务器的机密短语。确保每个nonce 都有一个有限的生命期(也就是过了一些时间后会失效,并且以后再也不会使用),而且是独一无二的(即任何其它的服务器都不能产生一个相同的nonce )。

  • Stale:不必须,一个标志,用来指示客户端先前的请求因其nonce值过期而被拒绝。如果stale是TRUE(大小写敏感),客户端可能希望用新的加密回应重新进行请求,而不用麻烦用户提供新的用户名和口令。服务器端只有在收到的请求nonce值不合法,而该nonce对应的摘要(digest)是合法的情况下(即客户端知道正确的用户名/口令),才能将stale置成TRUE值。如果stale是FALSE或其它非TRUE值,或者其stale域不存在,说明用户名、口令非法,要求输入新的值。

  • opaque(不透明体):必须的,这是一个不透明的(不让外人知道其意义)数据字符串,在盘问中发送给用户。

  • algorithm(算法):不必须,这是用来计算杂凑的算法。当前只支持MD5算法。

  • qop(保护的质量):必须的,这个参数规定服务器支持哪种保护方案。客户端可以从列表中选择一个。值 “auth”表示只进行身份查验, “auth-int”表示进行查验外,还有一些完整性保护。需要看更详细的描述,请参阅RFC2617

    1. 客户端反馈用户身份

    client 生成 生成摘要响应(digest response),然后再次通过 http request (INVITE (Withink digest))发给 server。

    摘要响应如 图三 中的Authenticate header所示:

    图三

    摘要响应中的各个参数意义如下:

  • username: 不用再说明了
  • realm: 需要和 server 盘问的realm保持一致
  • nonce:客户端使用这个“现时”来产生摘要响应(digest response),需要和server 盘问中携带的nonce保持一致,这样服务器也会在一个摘要响应中收到“现时”的内容。服务器先要检查了“现时”的有效性后,才会检查摘要响应的其它部分。

    因而,nonce 在本质上是一种标识符,确保收到的摘要机密,是从某个特定的摘要盘问产生的。还限制了摘要盘问的生命期,防止未来的重播攻击。

  • qop:客户端选择的保护方式。

  • nc (现时计数器):这是一个16进制的数值,即客户端发送出请求的数量(包括当前这个请求),这些请求都使用了当前请求中这个“现时”值。例如,对一个给定的“现时”值,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的,是让服务器保持这个计数器的一个副本,以便检测重复的请求。如果这个相同的值看到了两次,则这个请求是重复的。

  • response:这是由用户代理软件计算出的一个字符串,以证明用户知道口令。比如可以通过 username、password、http method、uri、以及nonce、qop等使用MD5加密生成。

  • cnonce:这也是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护。

  • uri:这个参数包含了客户端想要访问的URI。

    1. server 确认用户
      确认用户主要由两部分构成:
  • 检查nonce的有效性
  • 检查摘要响应中的其他信息, 比如server可以按照和客户端同样的算法生成一个response值,和client传递的response进行对比。

代码实现

Server 端

<?php

   // 解析PHP_AUTH_DIGEST
   function http_digest_parse($txt)
   {
    
       // 判断 Authorization数据是否完整
       $needed_parts = array(
           'nonce' => 1,
           'nc' => 1,
           'cnonce' => 1,
           'qop' => 1,
           'username' => 1,
           'uri' => 1,
           'response' => 1
       );
       $data = array();

       //把 txt 解析成了二维数组,结构 array(array('key','"','value'),...);
       preg_match_all('@(\w+)=([\'"]?)([a-zA-Z0-9=./\_-]+)\2@', $txt, $matches, PREG_SET_ORDER);

       foreach ($matches as $m) {
           //$m[1]是key值,$m[3]是value值
           $data[$m[1]] = $m[3];
           //将needed_parts中对应的key-value释放掉
           unset($needed_parts[$m[1]]);
       }

       //判断needed_parts是否已经被完全释放,如果是,则Authorization数据完整且解析成功,否则,解析失败
       return $needed_parts ? false : $data;
   }

    public function digest_authorization()
    {
    
        $realm = 'Restricted area';
        // username => password
        $users = array(
            
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在OkHttpClient中添加digest auth,你需要实现OkHttp的Authenticator接口,并将其设置到OkHttpClient中。具体的步骤如下: 1. 创建一个实现了Authenticator接口的类,例如DigestAuthenticator。 ```java public class DigestAuthenticator implements Authenticator { private final String username; private final String password; public DigestAuthenticator(String username, String password) { this.username = username; this.password = password; } @Override public Request authenticate(Route route, Response response) throws IOException { String challenge = response.header("WWW-Authenticate"); if (challenge == null || !challenge.startsWith("Digest ")) { // Challenge was not a digest authentication challenge. return null; } // Parse the challenge parameters. Map<String, String> params = parseChallengeParameters(challenge.substring("Digest ".length())); // Compute the response value. String uri = response.request().url().toString(); String realm = params.get("realm"); String nonce = params.get("nonce"); String qop = params.get("qop"); String nc = "00000001"; String cnonce = "0a4f113b"; String method = response.request().method(); String ha1 = md5(username + ":" + realm + ":" + password); String ha2 = md5(method + ":" + uri); String responseValue = md5(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2); // Build the authentication header. String headerValue = "Digest " + "username=\"" + username + "\", " + "realm=\"" + realm + "\", " + "nonce=\"" + nonce + "\", " + "uri=\"" + uri + "\", " + "qop=" + qop + ", " + "nc=" + nc + ", " + "cnonce=\"" + cnonce + "\", " + "response=\"" + responseValue + "\", " + "opaque=\"" + params.get("opaque") + "\""; // Return the authenticated request. return response.request().newBuilder() .header("Authorization", headerValue) .build(); } private Map<String, String> parseChallengeParameters(String challenge) { // Parse the challenge parameters. Map<String, String> params = new HashMap<>(); for (String param : challenge.split(",\\s*")) { int equalsIndex = param.indexOf('='); if (equalsIndex != -1) { String key = param.substring(0, equalsIndex).trim(); String value = param.substring(equalsIndex + 1).trim(); if (value.startsWith("\"") && value.endsWith("\"")) { value = value.substring(1, value.length() - 1); } params.put(key, value); } } return params; } private static String md5(String s) { try { MessageDigest digest = MessageDigest.getInstance("MD5"); byte[] bytes = digest.digest(s.getBytes("UTF-8")); return ByteString.of(bytes).hex(); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new AssertionError(e); } } } ``` 上述代码中的md5方法是用来计算MD5值的,使用了OkHttp中的ByteString类。 2. 创建一个OkHttpClient对象,并将DigestAuthenticator设置为它的Authenticator。 ```java OkHttpClient client = new OkHttpClient.Builder() .authenticator(new DigestAuthenticator("username", "password")) .build(); ``` 在创建OkHttpClient对象时,使用Builder模式设置了Authenticator,将DigestAuthenticator对象作为参数传递给它。 3. 使用OkHttpClient对象来发送请求。 ```java Request request = new Request.Builder() .url("http://example.com") .build(); Response response = client.newCall(request).execute(); ``` 在发送请求时,使用了OkHttpClient对象来发送请求。如果服务器需要digest auth认证,OkHttpClient会自动使用设置的Authenticator进行认证。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值