来自Keychain的Swift 4 WKWebView身份验证

问题描述 投票:1回答:1

我有一个非常简单的网页使用CakePHP 3.x,我写了一个简单的Swift 4应用程序来显示该网站。只要应用程序保持打开状态,会话将继续有效。应用程序关闭后,用户必须再次登录。我认为这是正确的行为,因为会话cookie存储在内存中并在浏览器关闭后被删除,但如果我错了就纠正我。我的用户想要登录一次,每次重新打开应用程序时都会自动登录。

我正在使用Swift 4和WKWebView。当用户提交登录表单时,我是否可以将用户凭据存储在特定URL的钥匙串中?我发现我可以使用Javascript来使用WKWebView here来操作表单字段,但这是使用swift 2或1Password。我还发现this post展示了如何使用钥匙串存储凭证。有点寻找两者的组合。我还需要知道我需要启用哪些plist才能使其工作。

我想要完成的步骤:

  1. 用户第一次打开应用程序,当WKWebView导航到主URL时,GET“/”。
  2. CakePHP看到用户未经授权并重定向到登录页面,GET“/ users / login”。
  3. 此时,我想检查密钥链中与此URL相关的凭据,但由于这是用户第一次打开应用程序,因此凭据不应存在。用户登录并单击提交,POST“/ users / login”。
  4. 在发布表单之前,请求在密钥链中存储凭据的权限。
  5. 用户关闭应用程序,清除会话。
  6. 用户打开应用程序,重复步骤1和2,但这次在步骤3中确实存在凭据。此时我想从钥匙串加载凭据,填写用户名和密码字段,然后触发提交。

这是我的视图控制器的副本:

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let url = URL(string: "https://www.example.com")!
        webView.load(URLRequest(url:url))
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func loadView() {
        webView = WKWebView()
        webView.navigationDelegate = self
        view = webView
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation) {
        title = webView.title
    }
}

编辑1:正如评论中所建议的那样,我通过检查请求标头并在令牌存在时设置会话来添加基于令牌的身份验证的功能。当用户第一次登录时,生成令牌并在响应中发回。我在ViewController中添加了另一个webView函数,以在响应头中查找令牌。

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    let token = (navigationResponse.response as! HTTPURLResponse).allHeaderFields["X-SAMPLE-TOKEN-HEADER"] as? String
    print(token)
    decisionHandler(.allow)
} 

编辑2:在Swift中,向URLRequest对象添加标题并不难。我已经更新了我的viewDidLoad(),以便在webView加载请求之前添加令牌标头。我更改了URL以直接进入登录页面。我已对此进行了测试,并且我的用户已获得授权,并且在标题存在时设置了会话。

override func viewDidLoad() {
    super.viewDidLoad()      
    var request = URLRequest(url:URL(string: "https://www.example.com/users/login")!)
    request.addValue("abcdef01234567890fedcba9871634556211", forHTTPHeaderField: "X-SAMPLE-TOKEN-HEADER")
    webView.load(request)
}

编辑3:使用此stackoverflow answer,我更新了我的ViewController以存储在响应中找到的令牌,并在webView加载URLRequest之前在标头中添加令牌。

let key = "com.example.www.token"
let header = "X-SAMPLE-TOKEN-HEADER"

override func viewDidLoad() {
    super.viewDidLoad()
    var request = URLRequest(url:URL(string: "https://www.example.com/users/login")!)
    let token = UserDefaults.standard.string(forKey: key)

    if (token != nil) {
        request.addValue(token!, forHTTPHeaderField: header)
    }

    webView.load(request)
}

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {

    let token = (navigationResponse.response as! HTTPURLResponse).allHeaderFields[header] as? String

    if (token != nil) {
        if (UserDefaults.standard.string(forKey: key) != nil) {
            UserDefaults.standard.removeObject(forKey: key)
        }

        UserDefaults.standard.set(token, forKey: key)
    }

    decisionHandler(.allow)
}

我愿意接受如何改善这一点的建议,但现在它符合预期。

authentication swift4 ios11 cakephp-3.x
1个回答
1
投票

根据建议使用基于令牌的身份验证,生成令牌并在用户登录时在响应头中传回。

WKNavigationDelegate有一个传递给WKNavigationResponse对象的method,它允许你查看响应,包括标题。

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    ... do stuff

    let headerValue = (navigationResponse.response as! HTTPURLResponse).allHeaderFields["X-HEADER-NAME"] as? String

    ... do more stuff
}

UserDefaults类可用于存储,检索或删除令牌。

let tokenKey = "unique.identifier"
let newToken = "NEWTOKEN12345"
let oldToken = UserDefaults.standard.string(forKey: tokenKey)

if (oldToken != nil) {
    UserDefaults.standard.removeObject(forKey: tokenKey)
}

UserDefaults.standard.set(token, forKey: tokenKey)

要仅为初始请求添加标头,可以在webView加载标头之前修改URLRequest对象以添加标头。

override func viewDidLoad() {
    ... do stuff

    let tokenKey = "unique.identifier"
    let token = UserDefaults.standard.string(forKey: tokenKey)

    var request = URLRequest(url:URL(string: "https://www.example.com/users/login")!)

    if (token != nil) {
        request.addValue(token!, forHTTPHeaderField: header)
    }

    ... do more stuff

    webView.load(request)
}
© www.soinside.com 2019 - 2024. All rights reserved.