NTLM 的 URLSession 的 401 挑战

Posted

技术标签:

【中文标题】NTLM 的 URLSession 的 401 挑战【英文标题】:401 challenges with URLSession for NTLM 【发布时间】:2019-09-13 21:52:08 【问题描述】:

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

let config = URLSessionConfiguration.default

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

【问题讨论】:

【参考方案1】:

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

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

以下代码已使用 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 行是在 Playground 中运行此代码所必需的,否则代码将无法在 Playground 终止之前完成运行。

【讨论】:

这并没有解决问题。这个解决方案仍然会为每个新网址带来 401 挑战

以上是关于NTLM 的 URLSession 的 401 挑战的主要内容,如果未能解决你的问题,请参考以下文章

使用 RxSwift 和 URLSession 处理 401 状态

JAX-WS Sharepoint 401 未经授权的 NTLM

401 客户端“协商”,服务器“协商,NTLM”在调用 WCF 服务器到服务器时

HttpClient 4.1.1 在使用 NTLM 进行身份验证时返回 401,浏览器工作正常

Javascript/Ajax NTLM 身份验证

核心中的 NTLM 身份验证 HttpClient