A-A+

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

博客主机

什么是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 和 怎么实现了。 感谢

标签:

给我留言

Copyright © ios教程,苹果粉丝,苹果资讯,ios入门教程,ios学习,ios程序员,ios视频教程,ios粉丝网 保留所有权利.   Theme  Ality

用户登录