带有令牌认证的 NSURLSession

Posted

技术标签:

【中文标题】带有令牌认证的 NSURLSession【英文标题】:NSURLSession with Token Authentication 【发布时间】:2014-01-04 11:21:04 【问题描述】:

我的ios 项目中有以下代码,我想转换为使用NSURLSession 而不是NSURLConnection。我正在查询一个使用基于令牌的HTTP Authentication 方案的REST API,但我找不到如何执行此操作的示例。

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];

NSString *username = [[NSUserDefaults standardUserDefaults] stringForKey:@"Username"];

NSString *token = //GET THE TOKEN FROM THE KEYCHAIN


NSString *authValue = [NSString stringWithFormat:@"Token %@",token];
[request setValue:authValue forHTTPHeaderField:@"Authorization"];


if ([NSURLConnection canHandleRequest:request])
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
    [NSURLConnection sendAsynchronousRequest:request queue:self.fetchQueue
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) 

                               if (!connectionError) 
                                   NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                                   if (httpResponse.statusCode == 200)
                                       NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:nil];

                                       //Process the data
                                   
                               

                           ];

【问题讨论】:

您使用什么请求方法,GET 或 POST 发送身份验证数据? 【参考方案1】:

你可以使用 NSURLSession 重写它,如下所示

    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

    NSString *token ; //GET THE TOKEN FROM THE KEYCHAIN

    NSString *authValue = [NSString stringWithFormat:@"Token %@",token];

    //Configure your session with common header fields like authorization etc
    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    sessionConfiguration.HTTPAdditionalHeaders = @@"Authorization": authValue;

    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

    NSString *url;
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) 
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
        if (!error) 
            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
            if (httpResponse.statusCode == 200)
                NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:nil];

                //Process the data
            
        

    ];
    [task resume];

【讨论】:

NSMutableURLRequest 的文档说:IMPORTANT The NSURLConnection class and NSURLSession classes are designed to handle various aspects of the HTTP protocol for you. As a result, you should not modify the following headers: Authorization, ...(参见:doc)。但是如果没有 [request setValue: ...] 那么怎么做呢? @PaulSpieker 您可以为常见的 HTTP 标头字段配置会话。我已经更新了我的答案。 NSURLSessionConfiguration 的文档也说了同样的话:An NSURLSession object is designed to handle various aspects of the HTTP protocol for you. As a result, you should not modify the following headers: Authorization, ... (doc)。你的方法安全吗? 我和@JacobDam 有同样的问题。我应该将 OAuth 2.0 身份验证标头 (Autorization: Bearer (token)) 传递给服务。我想我会这样做,如果它有效,请忽略 Apple 文档中的警告...... ...好吧,我的请求成功了,我在响应正文中得到了我期望的所有数据。也许 API 有一种推荐的、更合适的方式将身份验证标头信息(即令牌)传递给 NSURLSession/NSURLConnection,而不是直接弄乱标头?【参考方案2】:

这是在 Swift 中,但逻辑是一样的:

    let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
    let url  = NSURL(string: "some url")

    let request = NSMutableURLRequest(URL: url!)
    request.setValue("value", forHTTPHeaderField: "header field")

    let urlSession = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: NSOperationQueue.mainQueue())


    let dataTask = urlSession.dataTaskWithRequest(request)  (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
    

    dataTask.resume()

【讨论】:

【参考方案3】:

在您的 URLSession 配置中或直接在您的请求中设置 Authorization 标头。

请注意,Apple 表示您“不应”尝试modify the Authorization header in your URLSession configuration:

URLSession 对象旨在为您处理 HTTP 协议的各个方面。因此,您不应修改以下标头:

授权

...

但是,这是可能的。如果您想这样做,请确保在创建 URLSession 之前 设置标头配置。无法修改现有 URLSession 的标头。

这是一个展示不同场景的 Swift Playground:

import Foundation

// 1: Wrong -- mutating existing config for existing URLSession is no-op

var session = URLSession.shared
session.configuration.httpAdditionalHeaders = ["Authorization": "123"]

let url = URL(string: "https://postman-echo.com/get")!

session.dataTask(with: url)  (data, resp, err) in
    if let data = data 
        let str = String(bytes: data, encoding: .utf8)!
        let _ = str
    
.resume() // no authorization header

// 2: Setting headers on an individual request works fine.

var request = URLRequest(url: url)
request.addValue("456", forHTTPHeaderField: "Authorization")
session.dataTask(with: request)  (data, resp, err) in
    if let data = data 
        let str = String(bytes: data, encoding: .utf8)!
        let _ = str
    
.resume() // "headers":  "authorization": "456" 

// 3: You can set headers on a new URLSession & configuration

var conf = URLSessionConfiguration.default
conf.httpAdditionalHeaders = [
    "Authorization": "789"
]
session = URLSession(configuration: conf)

session.dataTask(with: url)  (data, resp, err) in
    if let data = data 
        let str = String(bytes: data, encoding: .utf8)!
        let _ = str
    
.resume() // "headers":  "authorization": "789" 

【讨论】:

第一个示例不起作用,因为在将 URLSessionConfiguration 添加到 URLSession 之后对其进行变异没有任何效果。在将 httpAdditionalHeaders 添加到 URLSession 之前设置它应该可以工作。 嗯,这个细节会为我省去很多麻烦和测试。 :) 谢谢@JonShier 我更新了我的帖子以反映这一点。

以上是关于带有令牌认证的 NSURLSession的主要内容,如果未能解决你的问题,请参考以下文章

使用 jwt 令牌认证识别用户

JWT 令牌是如何认证的?

Web API 2,令牌认证和授权

Spotify Web API - 没有令牌认证的请求

SPA-Auth0-API 令牌认证序列是啥?

为啥令牌认证比基于 API 密钥的认证更安全?