我有一个使用CakePHP 3.x的非常简单的网页,我写了一个简单的Swift 4应用程序来显示该网站。只要应用程序保持打开状态,会话就会继续工作。应用程序关闭后,用户必须再次登录。我相信这是正确的行为,因为会话 cookie 存储在内存中,并在浏览器关闭后被删除,但如果我错了,请纠正我。我的用户希望登录一次,每次重新打开应用时都自动登录。
我正在使用 Swift 4 和 WKWebView。当用户提交登录表单时,有没有办法将用户的凭据存储在特定 URL 的钥匙串中?我发现我可以使用Javascript通过WKWebView操作表单字段,但这使用的是swift 2或1Password。我还发现了这篇文章,展示了如何使用钥匙串来存储凭据。有点像寻找两者的结合。我还需要知道我需要启用哪些列表才能使其工作。
我正在尝试完成的步骤:
-
用户首次打开应用程序,当 WKWebView 导航到主 URL 时,GET "/"。
-
CakePHP 看到用户未获得授权并重定向到登录页面 GET "/users/login"。
-
此时,我想检查钥匙串中与此 URL 相关的凭据,但由于这是用户第一次打开应用程序,因此凭据应该不存在。用户登录并单击提交,发布"/用户/登录"。
在 发布表单之前,请请求在钥匙串中存储凭据的权限。
用户关闭应用,清除会话。
用户打开应用,重复步骤 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:如评论中所建议的,我通过检查请求标头并设置会话(如果令牌存在(添加了基于令牌的身份验证功能。当用户首次登录时,将生成令牌并在响应中发送回。我在视图控制器中添加了另一个 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:使用此堆栈溢出答案,我更新了我的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)
}
我愿意接受有关如何使其更好的建议,但现在它符合预期。
按照建议使用基于令牌的身份验证,在用户登录时生成令牌并在响应标头中传递回。
WKNavigationDelegate 有一个传入 WKNavigationResponse 对象的方法,它允许您查看响应,包括标头。
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
}
类可用于存储、检索或删除令牌。
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)
若要仅向初始请求添加标头,可以修改 URLRequest 对象以在 webView 加载标头之前添加标头。
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)
}