WKWebView 中注入 cookie

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// 解析 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 有两种方式,第一种,通过请求带过去

1
2
3
4
5
6
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 注入

1
2
3
4
5
6
7
8
9
10
11
12
13
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 的用法

1
2
3
4
5
6
7
8
var headers: [String : String]? {
switch self {
case .login, .checkUpdate, .idCodeImg:
return nil
default:
return (HTTPCookieStorage.shared.cookies ?? []).moya
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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: ";")
}
}

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