iOS实现HTTP认证之摘要认证-Digest

收录:shenshi_bing

什么是Digest?

摘要访问认证是一种协议规定的Web服务器用来同网页浏览器进行认证信息协商的方法。它在密码发出前,先对其应用哈希函数,这相对于HTTP基本认证发送明文而言,更安全。 从技术上讲,摘要认证是使用随机数来阻止进行密码分析的MD5加密哈希函数应用.

1. 基本流程

  1. 客户端发起GET(PUT、POST、DELETE…)请求
  2. 服务器响应401 Unauthorized,WWW-Authenticate指定认证算法,realm指定安全域
  3. 客户端重新发起请求,Authorization指定用户名和密码信息
  4. 服务器认证成功,响应200,可选Authentication-Info 如下图所示

2.basic和digest的区别

  • basic 将“用户名:密码”打包并采用Base-64编码。(提示:base64是可以直接解码的) 缺点:密码很容易被窥探,可以挟持编码后的用户名、密码信息,然后发给服务器进行认证;可以与SSL配合,隐藏用户名密码。 如果你使用的是AFNetworking网络框架只需要调用如下代码就能实现

    [_httpManager.requestSerializer setAuthorizationHeaderFieldWithUsername:@"username" password:@"password"];
    
  • digest 和basic不同的是,digest不以明文发送密码,在请求接口后服务器响应返回随机字符串nonce,而客户端发送响应摘要 response=MD5(HA1:nonce:HA2),其中HA1=MD5(username:realm:password), HA2=MD5(method:digestURI) 在HTTP 摘要认证中使用 MD5 加密是为了达成"不可逆"。 具体的加密方式不止MD5一种还有SHA256等加密方式,but计算公式是一样的

  • digest参数介绍 username: 用户名(一般是规定好的) password: 密码(同上) realm: 服务器返回的realm,一般是域名 method: 请求的方法 nonce: 服务器发给客户端的随机的字符串 nc(nonceCount):请求的次数,用于标记,计数,防止重放攻击 cnonce(clinetNonce): 客户端发送给服务器的随机字符串 qop: 保护质量参数,一般是auth,或auth-int,这会影响摘要的算法 uri: 请求的uri(只是path) response: 客户端根据算法算出的摘要值

3.具体操作流程

第一步

  • 首先我们需要做的是需要下载一些实用工具
  • 接口请求工具-postman
  • 抓包工具-Fiddler、Wireshark、Charles (选其一即可)

第二步

  • 配置postman设置,如下图注:Algorithm 选择自己需要的加密方式*

第三步

  • 打开抓包工具准备抓取请求
  • 点击postman工具中的蓝色按钮“send”发送请求

第四步

  • 查看抓包工具中的信息,如图

  • 标红的部分 WWW-Authenticate: Digest realm=“xxx.com”,nonce=“dcd1e62391252a6fae88fbec4485a32b”, algorithm=SHA-256 我们需要的做的就是把nonce字符串在下次请求的时候在header头部设置里传给服务器校验,如果校验成功就会返回200

  • 看一下200的那条数据返回,如图

  • 看一下Headers返回的数据 Authorization: Digest username=“ApiAdmin”, realm=“xxx.com”, nonce=“dcd1e62391252a6fae88fbec4485a32b”, uri=“xxxx?ChannelId=101”, algorithm=“SHA-256”, response=“c7ba2951cb51d32c1764e368813c139e8a4364807f7f2764ab7e1130341087fb” 这是一次正常的数据操作流程

第五步

  • 最重要的来了,那就是代码实现
  • 入坑经历,最重要!最重要!最重要!
  • 我项目中的一直使用的是AFNetworking网络库请求接口,我花费了将近一天的时间调试一直处于401状态码返回,也就是说一直没有调通!
  • 脱坑技巧,改用系统原生的的网络请求! 注:如果你使用AFNetworking网络框架并且成功实现,拜托一定要留言板留下你的代码实现,表示感谢!

第六步

代码实现 *注:nonceStr 声明一个全局的NSString来接收请求的nonce

-(void)getRequest{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
//以GET请求为例,以下方法为封装的配置方法
    [self setHttpHeaderDigestURI:identifier requestType:RequestType_GET nonce:self.nonceStr];
NSURLSession *session = [NSURLSession sharedSession];
//发送请求
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:self.request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSDictionary * pFieldd =[(NSHTTPURLResponse*)response allHeaderFields];
        NSInteger statusCode =[(NSHTTPURLResponse*)response statusCode];
        if (statusCode == 401) {//digest auth
            NSString * strAuthenticate = [pFieldd valueForKey:@"Www-Authenticate"];
            if ([strAuthenticate containsString:@"nonce="]) {
                NSArray *arr = [strAuthenticate componentsSeparatedByString:@"nonce="];
                NSArray *newArr = [[arr lastObject] componentsSeparatedByString:@","];
                NSMutableString *newStr = [NSMutableString stringWithString:[newArr firstObject]] ;
                if (newStr.length >= 3) {
                    [newStr replaceCharactersInRange:NSMakeRange(0, 1) withString:@""];
                    [newStr replaceCharactersInRange:NSMakeRange(newStr.length - 1, 1) withString:@""];
                }
                NSString *nonce = newStr ;
                weakself.nonceStr = nonce;
                weakself.count += 1;
                if (weakself.count > 3) {
                    weakself.count = 0;
                    [weakself error:error finishedBlock:finishedBlock];
                }else{
                    [weakself sendGetRequestWithParams:params getValues:values resultType:type finishedBlock:finishedBlock];
                }
            }else{
                [weakself error:error finishedBlock:finishedBlock];
            }
        }else{
            NSError *error;
            NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
            if (responseObject && [responseObject isKindOfClass:[NSDictionary class]]) {
                if (finishedBlock) {
                    finishedBlock(CODE_JSON_OK,responseObject);
                    NSError *error;
                    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:responseObject options:NSJSONWritingPrettyPrinted error:&error];
                    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
                }
            }else{
                if (finishedBlock) {
                    if (responseObject != nil) {
                        finishedBlock(CODE_ERROR_JSON, responseObject);
                    }else{
                        finishedBlock(CODE_ERROR_JSON, @"responseObject为nil");
                    }
                }
            }
        }
    }];
    //执行任务
    [dataTask resume];
    }
    //配置请求的header
-(void)setHttpHeaderDigestURI:(NSString *)digestURI requestType:(RequestType)requestType nonce:(NSString *)nonce{
    NSString *username = @"ApiAdmin";
    NSString *password = @"xxx";
    NSString *realm = @"xxx.com";
    NSString *method ;
    switch (requestType) {
        case RequestType_POST:{
            method = @"POST";
        }
            break;
        case RequestType_GET:{
            method = @"GET";
        }
            break;
        case RequestType_PUT:{
            method = @"PUT";
        }
            break;
        case RequestType_DELETE:{
            method = @"DELETE";
        }
            break;
        default:
            break;
    }
    //修改请求方法
    self.request.HTTPMethod = method;
    //此字符串可任意
    nonce = @"9e6146023a70bdb0b4d1da795a029990";
    if (nonce.length > 0) {
        nonce = self.nonceStr;
        self.nonceStr = nil;
    }
    //这里是SHA256加密如果你是MD5或者其他的可自由切换
    NSString *HA1 = [[NSString stringWithFormat:@"%@:%@:%@",username,realm,password] SHA256];
    NSString *HA2 = [[NSString stringWithFormat:@"%@:%@",method,digestURI] SHA256];
    NSString *sha265 = [NSString stringWithFormat:@"%@:%@:%@",HA1,nonce,HA2];
    NSString *algorithm = @"SHA-256";
    NSString *response = [sha265 SHA256];
    NSString *authorization = [NSString stringWithFormat:@"Digest username=\"%@\", realm=\"%@\", nonce=\"%@\", uri=\"%@\", algorithm=\"%@\", response=\"%@\"",username,realm,nonce,digestURI,algorithm,response];
    [self.request setValue:authorization forHTTPHeaderField:@"Authorization"];
    [self.request  setValue :@"application/soap+xml;charset=utf-8"  forHTTPHeaderField:@"Content-Type" ] ;
}

顺便送上SHA256加密的方法,新建一个NSString的category,实现方法如下

#import "NSString+SHA256.h"
#import <CommonCrypto/CommonDigest.h>

@implementation NSString (SHA256)

- (NSString *)SHA256
{
    const char *s = [self cStringUsingEncoding:NSUTF8StringEncoding];
    NSData *keyData = [NSData dataWithBytes:s length:strlen(s)];
    uint8_t digest[CC_SHA256_DIGEST_LENGTH] = {0};
    CC_SHA256(keyData.bytes, (CC_LONG)keyData.length, digest);
    NSData *out = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
    const unsigned *hashBytes = [out bytes];
    NSString *hash = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
                      ntohl(hashBytes[0]), ntohl(hashBytes[1]), ntohl(hashBytes[2]),
                      ntohl(hashBytes[3]), ntohl(hashBytes[4]), ntohl(hashBytes[5]),
                      ntohl(hashBytes[6]), ntohl(hashBytes[7])];
    return hash;
}

看到这里你基本上已经明白什么是Digest 和 怎么实现了。 感谢 https://www.jianshu.com/p/18fb07f2f65e 给我的一些帮助!

当你从不了解到熟悉的过程才是提升的过程,不要胆怯,勇敢的去追寻!

结交人脉

最后推荐个我的iOS交流群:789143298
'有一个共同的圈子很重要,结识人脉!里面都是iOS开发,全栈发展,欢迎入驻,共同进步!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值