Skip to content

WKWebView 中注入 cookie

轩辕十四
Published date:

在项目中,往往有这样的要求,用 API 进行登录之后在某个 WKWebView 的页面需要用 cookie 去验证身份(虽然我更喜欢用 accesstoken 去验证🙃)。WKWebView 是苹果官方建议的控件来替代老旧的 UIWebView。但是 WKWebViewcookie 无法共享 NSHTTPCookieStorage,所以这时候就需要我们自己去管理 cookie。(iOS 11 上新增了 WKHTTPCookieStore 来管理)

首先我们先解析获取到的 cookie

/// 解析 cookie
private func analysisCookie(response: HTTPURLResponse) {
    
    if let fields = response.allHeaderFields as? [String: String],
        let url = response.url
    {
        let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields,
                                         for: url)
        let storage = HTTPCookieStorage.shared
        for cookie in cookies {
            /// 将 cookie 存入本地
            storage.setCookie(cookie)
        }
    }
}

WKWebView 中注入 cookie 有两种方式,第一种,通过请求带过去

if let url = URL(string: "") {
    let reqURL = URLRequest(url: url)
    let cookie = (HTTPCookieStorage.shared.cookies ?? []).strCookie
    reqURL.addValue(cookie, forHTTPHeaderField: "cookie")
    webView.load(reqURL)
}

第二种方式,js 注入

private func createWeb() {
    let config = WKWebViewConfiguration()
    let cookie = (HTTPCookieStorage.shared.cookies ?? []).userScript
    let user = WKUserContentController()
    let cookieScript = WKUserScript(source: cookie,
                                    injectionTime: .atDocumentStart,
                                    forMainFrameOnly: false)
    user.addUserScript(cookieScript)
    config.userContentController = user
    webView = WKWebView(frame: self.view.bounds, configuration: config)
    self.view.addSubview(webView)
    webView.load(requestURL)
}

moya 的用法

var headers: [String : String]? {
    switch self {
    case .login, .checkUpdate, .idCodeImg:
        return nil
    default:
        return (HTTPCookieStorage.shared.cookies ?? []).moya
    }
}

这里我们需要将 HTTPCookie 类型转换一下,我们给 Array 添加几个扩展

extension Array where Element: HTTPCookie {
    
    /// 返回 moya 使用格式 cookie
    public var moya: [String: String] {
        var cookie: [String: String] = [:]
        cookie["Cookie"] = self.map { "\($0.name)=\($0.value)" }.joined(separator: ";")
        return cookie
    }
    
    /// 返回 js 注入的字符串格式 cookie
    public var userScript: String {
        return self.map { "document.cookie='\($0.name)=\($0.value)'" }.joined(separator: ";")
    }
    
    /// 返回字符串类型 cookie
    public var strCookie: String {
        return self.map { "\($0.name)=\($0.value)" }.joined(separator: ";")
    }
}

在我的项目中通过第二种方式实现了验证成功。

Previous
Swift 面向协议 - 为类提供基于 Storyboard 的初始化方法
Next
处理 initialize() 的弃用【译】