Swift 5 URLRequest授权标头:保留,如何设置?

时间:2019-05-17 22:17:14

标签: ios swift macos authentication http-headers

在Foundation的documentation for Swift's URLRequest中,它说不应为保留的HTTP标头使用设置URLRequest标头值的标准方法。

在文档中指向the list of reserved HTTP headers的链接稍深一些时,它表示可能会忽略设置这些标头的尝试。

但是它也说Authorization是保留的HTTP标头。

这不对,对吗? Universe中的很大一部分API要求您以Authorization: Bearer {token}

形式的标头传递身份验证令牌

因此,如果Swift不允许您设置Authorization标头,那么一个人如何访问这些API之一?

2 个答案:

答案 0 :(得分:0)

实施身份验证挑战以处理基本身份验证,例如:

func urlSession(_ session: URLSession,
                didReceive challenge: URLAuthenticationChallenge,
                completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    switch challenge.protectionSpace.authenticationMethod {
    case NSURLAuthenticationMethodHTTPBasic:
        performBasicAuthentication(challenge: challenge, completionHandler: completionHandler)
    default:
        completionHandler(.performDefaultHandling, nil)
    }
}

func performBasicAuthentication(challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    let credential = URLCredential(user: "username", password: "password", persistence: .forSession)
    completionHandler(.useCredential, credential)
}

这里是参考文献link

答案 1 :(得分:0)

按照你们所有人提到的文档,我现在结束了以下内容:

class ApiManager: NSObject {

    var credential: URLCredential?
    
    func token(withCredential credential: URLCredential?) {
        guard let url = URL(string: "\(K.API)/token") else {
            print("error URL: \(K.API)/token")
            return
        }

        self.credential = credential
        
        let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "accept")
        request.setValue("application/json", forHTTPHeaderField: "content-type")
        
        let task = session.dataTask(with: request) { (data, response, error) in
            self.credential = nil
            
            if error != nil {
                print("URLSession error: \(error!.localizedDescription)")
                return
            }
            
            guard let safeHttpResponse = response as? HTTPURLResponse else {
                print("HTTPURLResponse error: \(error!.localizedDescription)")
                return
            }
            
            if safeHttpResponse.statusCode == 200,
               let safeData = data,
               let dataString = String(data: safeData, encoding: .utf8) {
                print("safeData: \(dataString)")
            } else {
                print("error: \(safeHttpResponse.statusCode)")
            }
        }
        
        task.resume()
    }
    
}

这里,token 是一种方法,作为对用户进行身份验证的示例。

我将类似的东西从 UI 传递给这个方法

URLCredential(user: usernameTextField.text, password: passwordTextField.text, persistence: .forSession)

那么最重要的是URLSessionTaskDelegate

extension ApiManager: URLSessionTaskDelegate {
    // From https://developer.apple.com/forums/thread/68809
    // We should use session delegate as setting Authorization Header won't always work
    
    func urlSession(
        _ session: URLSession,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        // This method is called mainly with HTTPS url
        
        let protectionSpace = challenge.protectionSpace
        let authMethod = protectionSpace.authenticationMethod
        
        guard authMethod == NSURLAuthenticationMethodServerTrust, protectionSpace.host.contains(K.API.host) else {
            completionHandler(.performDefaultHandling, nil)
            return
        }
        
        guard let safeServerTrust = protectionSpace.serverTrust else {
            completionHandler(.performDefaultHandling, nil)
            return
        }
        
        DispatchQueue.global().async {
            SecTrustEvaluateAsyncWithError(safeServerTrust, DispatchQueue.global()) { (trust, result, error) in
                if result {
                    completionHandler(.useCredential, URLCredential(trust: trust))
                } else {
                    print("Trust failed: \(error!.localizedDescription)")
                    completionHandler(.performDefaultHandling, nil)
                }
            }
        }
    }
    
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        // This method is called for authentication
        
        let protectionSpace = challenge.protectionSpace
        let authMethod = protectionSpace.authenticationMethod
        
        switch (authMethod, protectionSpace.host) {
            case (NSURLAuthenticationMethodHTTPBasic, K.API.host):
                self.basicAuth(didReceive: challenge, completionHandler: completionHandler)
            // we could add other authentication e.g Digest
            default:
                completionHandler(.performDefaultHandling, nil)
        }
    }
    
    private func basicAuth(
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        
        if challenge.previousFailureCount < 3 {
            completionHandler(.useCredential, self.credential)
        } else {
            completionHandler(.cancelAuthenticationChallenge, nil)
        }
    }
    
}

我这样称呼一切:

let apiManager = ApiManager()
let credential = URLCredential(user: email, password: password, persistence: .forSession)
apiManager.token(withCredential: credential)

例如,我必须使用 completionHandler 处理响应,但请求已通过身份验证且有效