在 Swift 中使用 WKWebView 进行身份验证

Posted

技术标签:

【中文标题】在 Swift 中使用 WKWebView 进行身份验证【英文标题】:Authentication with WKWebView in Swift 【发布时间】:2016-05-12 05:10:03 【问题描述】:

在我的 ios 应用程序中,我想使用 WKWebView 在应用程序中包装外部 URL。此 URL 需要基本身份验证(它需要用户和密码凭据,如下图所示)。

经过一番调查,我正在尝试使用didReceiveAuthenticationChallenge 方法,以启用自动登录,所以我不明白它是如何工作的。

这是我的代码。

import UIKit
import WebKit

class WebViewController: UIViewController, WKNavigationDelegate 

    var webView: WKWebView?

    private var request : NSURLRequest 
        let baseUrl = "https://unimol.esse3.cineca.it/auth/Logon.do"
        let URL = NSURL(string: baseUrl)!
        return NSURLRequest(URL: URL)
    

    /* Start the network activity indicator when the web view is loading */
    func webView(webView: WKWebView,
                 didStartProvisionalNavigation navigation: WKNavigation)
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    

    /* Stop the network activity indicator when the loading finishes */
    func webView(webView: WKWebView,
                 didFinishNavigation navigation: WKNavigation)
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    

    func webView(webView: WKWebView,
                 decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse,
                                                   decisionHandler: ((WKNavigationResponsePolicy) -> Void))
        decisionHandler(.Allow)
    

    func webView(webView: WKWebView, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) 

        if challenge.protectionSpace.host == "https://unimol.esse3.cineca.it/auth/Logon.do" 
            let user = "*******"
            let password = "******"
            let credential = NSURLCredential(user: user, password: password, persistence: NSURLCredentialPersistence.ForSession)
            challenge.sender?.useCredential(credential, forAuthenticationChallenge: challenge)
        
    

    override func viewDidLoad() 
        /* Create our preferences on how the web page should be loaded */
        let preferences = WKPreferences()
        preferences.javascriptEnabled = false

        /* Create a configuration for our preferences */
        let configuration = WKWebViewConfiguration()
        configuration.preferences = preferences

        /* Now instantiate the web view */
        webView = WKWebView(frame: view.bounds, configuration: configuration)

        if let theWebView = webView 
            /* Load a web page into our web view */
            let urlRequest = self.request
            theWebView.loadRequest(urlRequest)
            theWebView.navigationDelegate = self
            view.addSubview(theWebView)
        
    

我正面临这个例外:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Completion handler passed to -[MyUnimol.WebViewController webView:didReceiveAuthenticationChallenge:completionHandler:] was not called'

如果我删除 didReceiveAuthenticationChallenge 方法,我可以访问该 URL,但它给了我,不知不觉,错误的凭据。

谁能解释一下我做错了什么?

【问题讨论】:

【参考方案1】:

添加以下行:

completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential)

最后didReceiveAuthenticationChallenge解决了这个问题。

【讨论】:

【参考方案2】:

我迟到了,但这仍然对某人有用。

为了支持 WKWebview Swift 4 中的身份验证挑战,完成代码如下

func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 
        guard let hostname = webView.url?.host else 
            return
        

        let authenticationMethod = challenge.protectionSpace.authenticationMethod
        if authenticationMethod == NSURLAuthenticationMethodDefault || authenticationMethod == NSURLAuthenticationMethodHTTPBasic || authenticationMethod == NSURLAuthenticationMethodHTTPDigest 
            let av = UIAlertController(title: webView.title, message: String(format: "AUTH_CHALLENGE_REQUIRE_PASSWORD".localized, hostname), preferredStyle: .alert)
            av.addTextField(configurationHandler:  (textField) in
                textField.placeholder = "USER_ID".localized
            )
            av.addTextField(configurationHandler:  (textField) in
                textField.placeholder = "PASSWORD".localized
                textField.isSecureTextEntry = true
            )

            av.addAction(UIAlertAction(title: "BUTTON_OK".localized, style: .default, handler:  (action) in
                guard let userId = av.textFields?.first?.text else
                    return
                
                guard let password = av.textFields?.last?.text else 
                    return
                
                let credential = URLCredential(user: userId, password: password, persistence: .none)
                completionHandler(.useCredential,credential)
            ))
            av.addAction(UIAlertAction(title: "BUTTON_CANCEL".localized, style: .cancel, handler:  _ in
                completionHandler(.cancelAuthenticationChallenge, nil);
            ))
            self.parentViewController?.present(av, animated: true, completion: nil)
        else if authenticationMethod == NSURLAuthenticationMethodServerTrust
            // needs this handling on iOS 9
            completionHandler(.performDefaultHandling, nil);
        else
            completionHandler(.cancelAuthenticationChallenge, nil);
        
    

【讨论】:

【参考方案3】:

以更简单的方式:

虽然这个答案看起来多余,但发布,因为这可能会帮助其他天真的开发者(比如我)。

   func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 
        guard (webView.url?.host) != nil else 
            return
        
        let authenticationMethod = challenge.protectionSpace.authenticationMethod
        if authenticationMethod == NSURLAuthenticationMethodDefault || authenticationMethod == NSURLAuthenticationMethodHTTPBasic || authenticationMethod == NSURLAuthenticationMethodHTTPDigest 
            let credential = URLCredential(user: userName, password: password, persistence: .none)
            completionHandler(.useCredential, credential)
         else if authenticationMethod == NSURLAuthenticationMethodServerTrust 
            completionHandler(.performDefaultHandling, nil)
         else 
            completionHandler(.cancelAuthenticationChallenge, nil)
        
    

【讨论】:

我们在使用SSafariViewController的时候有没有办法处理。 @preetam

以上是关于在 Swift 中使用 WKWebView 进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中使用 JS 隐藏 WKWebView 集合元素

在 WKWebView (iOS, Swift) 中使用本地 Javascript 源加载本地 HTML

WKWebView:表单未在 WKWebView (Swift) 中提交

使用 evaluateJavascript 从 WKWebView 获取 HTML,然后将其存储在变量中(使用 Swift)

Swift:WKWebView 中 UIRefreshControl 的自定义操作

如何使用 swift 在 WKWebView 的页面中间显示活动指示器?