用于NTLM的URLSession带来的401个挑战

时间:2019-09-13 21:52:08

标签: swift

我正在使用带有委托的自定义URLSession来处理从我们的NTLM身份验证Web API收到的401质询。我希望只收到第一个api调用的挑战,但是我为调用的每个唯一端点得到一个挑战。有什么方法可以在每次调用时提供存储的凭据,从而避免每次都出现401?我的基本会话设置如下。

let config = URLSessionConfiguration.default

let delegate = MySessionDelegate()
let session = URLSession(configuration: config, delegate: delegate, delegateQueue: nil)

1 个答案:

答案 0 :(得分:0)

这是我使用NTLM身份验证处理API时发现的解决方案。本质上,您需要将URLSessionConfiguration实例的委托设置为ViewController的类(或任何继承NSObject的类)并实现func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)协议的URLSessionDelegate功能。 / p>

身份验证的处理方式是,当URLSession发出请求时,它首先协商身份验证方法(请参阅NSURLProtectionSpace Authentication Method Constants),然后根据您选择的身份验证方法(在这种情况下,我们拒绝所有内容)不是NSURLAuthenticationMethodNTLM),我们就通过服务器进行了身份验证。

以下代码已通过Swift 5.1和Xcode 11进行了测试:

import Cocoa
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class NTLMAuthentication: NSObject {

    private let baseURL = "<insert base URL here>"

    private let username: String
    private let password: String

    lazy private var session: URLSession = {
        let config = URLSessionConfiguration.default
        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()

    init(username: String, password: String) {
        self.username = username
        self.password = password
    }

    func authenticate() {
        let url = URL(string: baseURL)
        let request = URLRequest(url: url!)

        let task = session.dataTask(with: request) { data, response, error in
            guard error == nil else {
                print("An error occurred")
                return
            }

            let htmlData = String(bytes: data!, encoding: String.Encoding.utf8)
            print(htmlData)
        }
        task.resume()
    }
}

extension NTLMAuthentication : URLSessionDelegate {
    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

        print("got challenge")
        print(challenge.protectionSpace.authenticationMethod)

        guard challenge.previousFailureCount == 0 else {
            print("too many failures")
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }

        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNTLM else {
            completionHandler(.rejectProtectionSpace, nil)
            return
        }

        let credentials = URLCredential(user: self.username, password: self.password, persistence: .forSession)
        completionHandler(.useCredential, credentials)
    }
}

请注意,PlaygroundPage.current.needsIndefiniteExecution = true行是在游乐场中运行此代码所必需的,否则该代码将无法在游乐场终止之前完成运行。