Cookie handling

When we set cookies, what we usually do is we put cookies in the header of the request, but that just sends cookies to the server, we don’t save cookies locally, we end up writing cookies to a Cookie file directory in the WebView, Only the subsequent requests or jumps initiated by WebView can get the Cookie from the corresponding domain name when the request is initiated.

When the Webview loads the H5 page, it actually downloads the relevant.HTML, JS and CSS files to the local and then loads them. When the page goes to get cookies, it goes to the Cookie file directory in the local Webview to search for cookies. If it is not set, it will certainly not be found. So when setting cookies, both the server and client need to set cookies.

Cookie setting on the server

When using UIWebView, we manage cookies through NSHTTPCookieStorage. Now we add a Cookie named user to devqiaoyu.tech.

var props = Dictionary<HTTPCookiePropertyKey, Any>()
props[HTTPCookiePropertyKey.name] = "user"
props[HTTPCookiePropertyKey.value] = "admin"
props[HTTPCookiePropertyKey.path] = "/"
props[HTTPCookiePropertyKey.domain] = "devqiaoyu.tech"
props[HTTPCookiePropertyKey.version] = "0"
props[HTTPCookiePropertyKey.originURL] = "devqiaoyu.tech"
if let cookie = HTTPCookie(properties: props) {
    HTTPCookieStorage.shared.setCookie(cookie)
}
Copy the code

WKWebView CookieThe problem is thatWKWebViewInitiated requests are not automatically stored inNSHTTPCookieStorageIn a containerCookie.

The solution is simply to read the Cookie from NSHTTPCookieStorage before WKWebView initiates the request, and then manually add the Cookie to the URLRequest header.

func getCookie() -> String { var cookieString = "" if let cookies = HTTPCookieStorage.shared.cookies { for cookie in cookies { if cookie.domain == cookieDomain { let str = "\(cookie.name)=\(cookie.value)" cookieString.append("\(str);" ) } } return cookieString } var request = URLRequest(url: URL(string: "https://devqiaoyu.com")) request.addValue(getCookie(), forHTTPHeaderField: "Cookie")Copy the code

When the server page is redirected, the Cookie written in the RequestHeader for the first time will be lost and the redirected request will need to be added again. For details, please read below

Second, client Cookie setting

When the page loads, the backend, regardless of the language, can see the Cookie in the request header, but when the backend rendering returns to the page, when the client’s WebView is running, the document. Cookie API can not read the Cookie when the JS executes. So you have to deal with client-side cookies as well.

var cookieString = "" if let cookies = HTTPCookieStorage.shared.cookies { for cookie in cookies { if cookie.domain == "devqiaoyu.tech" { let str = "\(cookie.name)=\(cookie.value)" cookieString.append("document.cookie='\(str); path=/; domain=devqiaoyu.tech';" ) } } } let cookieScript = WKUserScript(source: cookieString, injectionTime: .atDocumentStart, forMainFrameOnly: false) let userContentController = WKUserContentController() userContentController.addUserScript(cookieScript) let webViewConfig = WKWebViewConfiguration() webViewConfig.userContentController = userContentController let webV = WKWebView(frame: CGRect.zero, configuration: webViewConfig)Copy the code

** Client-side Cookie injection is essentially creating a JAVASCRIPT script and letting the WebView execute it. It is recommended to inject static JS at.atdocumentStart. ** So that the WebView can get the Cookie that holds the client when loading the static page returned from the back end.

Note: Document.cookie () cannot set cookies across domains. For example, if your first loaded request www.baidu.com is redirected to www.google.com, the second request may not be accessible because it does not carry a cookie. Of course, there are solutions, please read on

The URL to intercept

In WKWebView, the following callback functions are called before each page jump:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)
Copy the code

Web page redirection problem

There are two types of redirect problems:

  • The server page is redirected and the newly initiated request needs to be replantedCookie
  • Local page redirection, as long as the client is set upCookieThen there is no need to deal with it

So if the server page is being redirected, determine whether the Request has the Cookie you want, Cancel it, modify the Request and start again.

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { var shouldCancelLoadURL = false if let cookie = navigationAction.request.value(forHTTPHeaderField: "Cookie") { if cookie.contains("user") { shouldCancelLoadURL = false } else { var request = URLRequest(url: URL(string: (navigationAction.request.url? .absoluteString)!) !). request.addValue(getCookie(), forHTTPHeaderField: "Cookie") webView.load(request) shouldCancelLoadURL = true } } else { var request = URLRequest(url: URL(string: (navigationAction.request.url? .absoluteString)!) !). request.addValue(getCookie(), forHTTPHeaderField: "Cookie") webView.load(request) shouldCancelLoadURL = true } if shouldCancelLoadURL { decisionHandler(WKNavigationActionPolicy.cancel) } else { decisionHandler(WKNavigationActionPolicy.allow) } }Copy the code

Cross-domain problem

For cross-domain problems, the solution is similar to the above method, but the judgment conditions are different.

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { var shouldCancelLoadURL = false if let url = navigationAction.request.url? AbsoluteString {if url.contains("devqiaoyu.tech") {shouldCancelLoadURL = false} else {absoluteString {if url.contains("devqiaoyu.tech") { Cookie shouldCancelLoadURL = true}} else { Kind of Cookie shouldCancelLoadURL = true} if shouldCancelLoadURL {decisionHandler (WKNavigationActionPolicy. Cancel)} else { decisionHandler(WKNavigationActionPolicy.allow) } }Copy the code

Request interception for bogus jumps

A JS call Native communication scheme, detailed introduction can be seen from zero to clean up a hybrid framework (I) — from the selection of JS communication scheme to start. The following is an excerpt from the article.

Bogus jump interception is when a web page makes a new jump request to an invalid address that does not exist at all, for example

/ / regular Http address https://wenku.baidu.com/xxxx?xx=xx/request/false communication wakaka: / / wahahalalala/action? Param = paramobjCopy the code

So if YOU look at the fake redirect address that I wrote down here, it’s a bullshit address that doesn’t mean anything, and you just put it in the browser, you just throw it in the WebView, you can’t open anything, but if you do it in the Hybrid WebView, you intercept it and you don’t redirect it, right

The URL is divided into several parts

  • Agreement: i.ehttp/https/fileWait, it did wakaka
  • Domain name: abovewenku.baidu.comwahahalalala
  • Path: abovexxxx?The action?
  • Parameters: abovexx=xxparam=paramobj

If we build a fake URL

  • Use protocol and domain name as communication identification
  • Identify the path as an instruction
  • Pass parameters as data

The client will block all requests without discrimination, and the real URL address should be spared as usual. Only the URL address matching the protocol domain name should be intercepted by the client. The intercepted URL will not cause the WebView to continue to jump to the wrong address, so there is no awareness. On the contrary, we can read the path of the intercepted URL as an instruction, and read the parameters as data, so as to call the corresponding Native code according to the convention

In fact, the above is a protocol convention. As long as the JS side presses the convention protocol to generate false URL, and Native intercepts/reads false URL according to the convention protocol, the whole process can run.

The user-agent Settings

Global Settings

That is, the User-Agent of all Web requests in the App has been modified.

// UIWebView let webView = UIWebView(frame: CGRect.zero) let userAgent = webView.stringByEvaluatingJavaScript(from: "navigator.userAgent") if let agent = userAgent { let user = "@\(agent); extra_user_agent" let dict = ["UserAgent":user] UserDefaults.standard.register(defaults: dict) } // WKWebView let webV = WKWebView(frame: CGRect.zero) webV.evaluateJavaScript("navigator.userAgent") { (result, error) in if let oldAgent = result as? String { let user = "@\(oldAgent); extra_user_agent" let dict = ["UserAgent":user] UserDefaults.standard.register(defaults: dict) } }Copy the code

Single WebView Settings

In iOS9, WKWebView provides a very convenient property to change user-agent, which is the customUserAgent property. This method is not only convenient to use, but also does not change the User Agent globally. Unfortunately, iOS9 only has this method, if iOS8, still need to use the above method.

let webView = UIWebView(frame: CGRect.zero) let userAgent = webView.stringByEvaluatingJavaScript(from: "navigator.userAgent") if let agent = userAgent { let user = "@\(agent); extra_user_agent" webView.customUserAgent = user }Copy the code

Refer to the article

WKWebView the pit