今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

更新:SDWebImage 添加 token

背景#

网上搜到的关于 SDWebImage 添加 token,亦或者 SDWebImage add header 的方法,都是直接使用SDWebImageDownloader中的setValue:forHTTPHeaderField:方法来设置。但是设置了之后笔者这边图片还是出不来,仔细研究后发现笔者这边的图片显示是先经过一次 302 跳转,然后跳转后才是真正的图片链接,第二次的这个链接是需要 token 的。

而直接设置SDWebImageDownloaderHTTPHeaderField设置到了第一个链接上面,302 重定向后第二个链接的HTTPHeaderField仍是没有 token

解决方法#

一般来说,直接使用SDWebImageDownloader中的setValue:forHTTPHeaderField:方法设置即可。如下:


- (void)addSDWebImageToken {
    SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    NSString *tokenString =[[NSUserDefaults standardUserDefaults]valueForKey:YourToken];
    tokenString = [NSString stringWithFormat:@"Bearer %@",tokenString];
    [downloader setValue:tokenString forHTTPHeaderField:@"Authorization"];
}

对于经过 302 重定向的链接,则需要如下修改:

打开SDWebImageDownloader.m类,修改- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler方法如下:


- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler {
    
    // Identify the operation that runs this task and pass it the delegate method
    NSOperation<SDWebImageDownloaderOperation> *dataOperation = [self operationWithTask:task];
    NSMutableURLRequest *customRequest = [[NSMutableURLRequest alloc] initWithURL:request.URL];
    customRequest.allHTTPHeaderFields = self.HTTPHeaders;
    if ([dataOperation respondsToSelector:@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)]) {
        [dataOperation URLSession:session task:task willPerformHTTPRedirection:response newRequest:customRequest completionHandler:completionHandler];
    } else {
        if (completionHandler) {
            completionHandler(customRequest);
        }
    }
}

** 原理:** 简单的说是把 重定向的 request 变为 CustomRequest, 然后给 CustomRequest 添加 header,最后回调 CustomRequest,而给 CustomRequest 设置 Header 的方法,是使用SDWebImageDownloadersetValue:forHTTPHeaderField:设置的。

优化#

上面的方法需要直接修改 SDWebImage类库,如果后续更新类库,则可能丢失或者每次都要修改一次。所以需要一个更好的解决方法:
使用 SwizzleMethod hook 替换 SDWebImageDownloader.m类中- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler这个方法,代码如下:

// 使用 Aspects 库
#import <Aspects/Aspects.h>
- (void)setSDWebImageToken {
    NSError *error;
    [[SDWebImageDownloader sharedDownloader] aspect_hookSelector:@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> aspectInfo){
        NSArray * param = aspectInfo.arguments;
        if (param && param.count > 3) {
            NSURLRequest * request = param[3];
            NSString *hostStr = request.URL.host;
            if ([self isUrlHostSameWithAppHost:hostStr]) {
                // 这里判断只有域名和公司域名相同,才处理重定向,否则不处理
                NSURLSessionTask * task = param[1];
                NSMutableURLRequest *customRequest = [[NSMutableURLRequest alloc] initWithURL:request.URL];
                customRequest.allHTTPHeaderFields = task.currentRequest.allHTTPHeaderFields;
                request = customRequest;
                NSInvocation * invocation = aspectInfo.originalInvocation;
                [invocation setArgument:&request atIndex:5];
                [invocation invoke];
            }
        }
    } error:&error];
}

更进一步,假如某些图片链接需要 token,某些不需要,要怎么处理。只修改上面的方法不行,因为上面的方法只有重定向 URL 才会访问,非重定向的链接不会走上面的方法,所以需要找到非重定向的链接走的方法,然后在那个方法里处理。

查看 SDWebImageDownloader类中的方法后发现,在请求的 header 的赋值是在createDownloaderOperationWithUrl:options:context:方法中,如下:

    xxx
    mutableRequest.allHTTPHeaderFields = self.HTTPHeaders;
    xxx

而给链接添加 token 的方法是全局调用的,传值 token 到 header 中,所以想要控制某些链接不加 token,某些添加,就需要在createDownloaderOperationWithUrl:options:context:这个方法之前控制,使用 AspectPositionBegore 参数,hook 这个方法保证在 header 赋值到 request 之前,判断域名决定是否添加 token,代码如下:


    NSError *noRedirectError;
    [[SDWebImageDownloader sharedDownloader] aspect_hookSelector:@selector(createDownloaderOperationWithUrl:options:context:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo) {
        NSArray *param = aspectInfo.arguments;
        if (param && param.count > 2) {
            NSURL *url = param[0];
            NSString *hostStr = url.host;
            if ([self isUrlHostSameWithAppHost:hostStr]) {
                [downloader setValue:tokenString forHTTPHeaderField:@"Authorization"];
            }
            else {
                [downloader setValue:nil forHTTPHeaderField:@"Authorization"];
            }
        }
    } error:&noRedirectError];


完整代码如下:



- (void)setSDWebImageToken {
    NSString *tokenString = @"xxx";
    if (IsNilString(tokenString)) {
        return;
    }
    SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    tokenString = [NSString stringWithFormat:@"Bearer %@",tokenString];
    [downloader setValue:tokenString forHTTPHeaderField:@"Authorization"];
    
    NSError *noRedirectError;
    [[SDWebImageDownloader sharedDownloader] aspect_hookSelector:@selector(createDownloaderOperationWithUrl:options:context:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo) {
        NSArray *param = aspectInfo.arguments;
        if (param && param.count > 2) {
            NSURL *url = param[0];
            NSString *hostStr = url.host;
            if ([self isUrlHostSameWithAppHost:hostStr]) {
                [downloader setValue:tokenString forHTTPHeaderField:@"Authorization"];
            }
            else {
                [downloader setValue:nil forHTTPHeaderField:@"Authorization"];
            }
        }
    } error:&noRedirectError];
    
    NSError *error;
    [[SDWebImageDownloader sharedDownloader] aspect_hookSelector:@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> aspectInfo){
        NSArray * param = aspectInfo.arguments;
        if (param && param.count > 3) {
            NSURLRequest * request = param[3];
            NSString *hostStr = request.URL.host;
            if ([self isUrlHostSameWithAppHost:hostStr]) {
                NSURLSessionTask * task = param[1];
                NSMutableURLRequest *customRequest = [[NSMutableURLRequest alloc] initWithURL:request.URL];
                customRequest.allHTTPHeaderFields = task.currentRequest.allHTTPHeaderFields;
                request = customRequest;
                NSInvocation * invocation = aspectInfo.originalInvocation;
                [invocation setArgument:&request atIndex:5];
                [invocation invoke];
            }
        }
    } error:&error];
}

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.