如何在WKWebView或iOS端PWA中支持WebAuthn及跨组件数据共享?
解决方案:WKWebView中WebAuthn失效及SFSafariViewController与WKWebView数据共享
一、WKWebView不支持WebAuthn的可行方案
1. 升级iOS/iPadOS版本到16.4+
iOS 16.4和iPadOS 16.4正式为WKWebView添加了WebAuthn支持,如果你的App目标用户系统版本可以覆盖到这个范围,直接升级系统后就能让原PWA的WebAuthn功能正常工作,无需额外修改。
2. 改用SFSafariViewController承载认证流程
SFSafariViewController完全支持WebAuthn,这是你已经尝试的方案,重点解决后续的数据传递问题,详见第二部分。
3. 原生封装WebAuthn能力,通过JS桥与WKWebView交互
如果必须用WKWebView,可以用苹果原生的LAContext(LocalAuthentication)实现生物识别,再通过WKWebView的WKScriptMessageHandler搭建JS与原生的通信桥:
- 原生端实现生物识别逻辑,生成或验证WebAuthn所需的凭证
- 在WKWebView中注册JS消息处理器,网页端触发生物识别请求
- 原生完成认证后,将结果通过
evaluateJavaScript(_:)注入到网页中
二、SFSafariViewController与WKWebView的数据共享方法
以下是几种可靠的实现方式,按安全性和易用性排序:
1. 自定义URL Scheme(最直接的跳转回调)
步骤:
- 在Xcode项目的「Info」→「URL Types」中添加自定义Scheme(比如
yourapp-auth) - 在认证页面完成后,通过JS跳转到该Scheme并携带参数:
// 认证成功后执行 const authToken = "获取到的认证凭证"; window.location.href = `yourapp-auth://complete?token=${encodeURIComponent(authToken)}`; - 在App的
SceneDelegate(或AppDelegate)中拦截URL,解析参数并传递给WKWebView:func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let url = URLContexts.first?.url, url.scheme == "yourapp-auth" else { return } // 解析查询参数 let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let token = components?.queryItems?.first(where: { $0.name == "token" })?.value ?? "" // 获取当前WKWebView实例,注入数据到网页 if let webView = (scene as? UIWindowScene)?.windows.first?.rootViewController?.view as? WKWebView { webView.evaluateJavaScript("window.handleAuthSuccess('\(token)')", completionHandler: { error in if let error = error { print("注入JS失败:\(error)") } }) } } - 网页端提前定义
handleAuthSuccess函数接收凭证:function handleAuthSuccess(token) { // 用token完成登录逻辑 console.log("收到认证凭证:", token); }
2. 共享Keychain存储(适合敏感数据)
因为SFSafariViewController和WKWebView同属一个App进程,可通过Keychain安全存储认证信息:
- 在原生端封装Keychain读写方法,比如:
class KeychainHelper { static func save(key: String, value: String) { let data = value.data(using: .utf8)! let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key, kSecValueData as String: data ] SecItemDelete(query as CFDictionary) SecItemAdd(query as CFDictionary, nil) } static func load(key: String) -> String? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key, kSecReturnData as String: kCFBooleanTrue!, kSecMatchLimit as String: kSecMatchLimitOne ] var data: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &data) guard status == errSecSuccess, let data = data as? Data else { return nil } return String(data: data, encoding: .utf8) } } - 在SFSafariViewController加载的页面中,通过JS桥调用原生保存凭证:
window.webkit.messageHandlers.saveAuthToken.postMessage({ token: "敏感凭证" }); - 原生处理消息并保存到Keychain:
class AuthMessageHandler: NSObject, WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "saveAuthToken", let body = message.body as? [String: String], let token = body["token"] { KeychainHelper.save(key: "auth_token", value: token) } } } - 返回WKWebView后,网页端调用原生读取Keychain数据:
window.webkit.messageHandlers.getAuthToken.postMessage(null); - 原生读取后注入网页:
// 在getAuthToken的消息处理中 if let token = KeychainHelper.load(key: "auth_token") { webView.evaluateJavaScript("window.receiveAuthToken('\(token)')", completionHandler: nil) }
3. 共享UserDefaults(适合非敏感数据)
原理和Keychain类似,但UserDefaults是明文存储,只适合非敏感的临时数据:
- 认证成功后,通过JS桥将数据存入UserDefaults:
UserDefaults.standard.set(token, forKey: "temp_auth_token") - 返回WKWebView后,网页端通过JS桥读取或由原生主动注入数据。
4. 利用SFSafariViewController代理回调
实现SFSafariViewControllerDelegate的safariViewControllerDidFinish(_:)方法,在用户点击「完成」按钮时触发数据同步:
func safariViewControllerDidFinish(_ controller: SFSafariViewController) { // 读取提前存储的认证数据(比如Keychain/UserDefaults) if let token = KeychainHelper.load(key: "auth_token") { // 传递给WKWebView webView.evaluateJavaScript("window.handleAuthSuccess('\(token)')", completionHandler: nil) // 用完清空临时存储 KeychainHelper.save(key: "auth_token", value: "") } }
内容的提问来源于stack exchange,提问作者Abhi
相关产品推荐
相关产品推荐

