由于授权,WKWebview 无法加载资产文件
Posted
技术标签:
【中文标题】由于授权,WKWebview 无法加载资产文件【英文标题】:WKWebview could not load asset files because of Authorization 【发布时间】:2020-12-18 16:51:18 【问题描述】:我有一个应用 AWS Cognito 的 WKWebview。 对服务器的每个请求都必须在请求头中添加授权。
let access_token = "Bearer \(key)"
let header: [String: String] = [
"Authorization": access_token
]
if let url = URL(string: "https://myserverdomain.amazonaws.com/api/v3/graphs?date=2020-08-28")
var request: URLRequest = URLRequest(url: url)
request.allHTTPHeaderFields = header
wkWebview.load(request)
使用此代码,我已经可以在页面中加载页面内容但 CSS。我检查了 chrome(使用 ModHeader chrome 扩展来添加标题),它工作正常,显示正确,也适用于 android。
我用Chrome和
标签中的CSS链接这样检查,和html文件不是同一个文件夹(不知道是不是这个原因)。<link rel="stylesheet" type="text/css" href="https://myserverdomain.amazonaws.com/assets/graphs/style.css"></script>
我只能用代码加载css内容:
let access_token = "Bearer \(key)"
let header: [String: String] = [
"Authorization": access_token
]
if let url = URL(string: "https://myserverdomain.amazonaws.com/assets/graphs/style.css")
var request: URLRequest = URLRequest(url: url)
request.allHTTPHeaderFields = header
wkWebview.load(request)
UIWebview 已被弃用,有没有办法像往常一样使用全局标题设置 WKWebview?
感谢您的帮助。
【问题讨论】:
【参考方案1】:您可以使用您的配置将所有 webview 的请求重定向到您的 URLSession
。为此,您可以为https
方案注册您的自定义URLProtocol
。 WKWebView
有一个 hack 来拦截带有 WKBrowsingContextController
私有类和您的 URLProtocol
实现的 url 请求,例如:
class MiddlewareURLProtocol : URLProtocol
static let handledKey = "handled"
lazy var session : URLSession =
// Config your headers
let configuration = URLSessionConfiguration.default
//configuration.httpAdditionalHeaders = ["Authorization" : "..."]
return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
()
var sessionTask : URLSessionTask?
override var task: URLSessionTask?
return sessionTask
static func registerClass()
let sel = NSSelectorFromString("registerSchemeForCustomProtocol:")
if let cls = NSClassFromString("WKBrowsingContextController") as? NSObject.Type, cls.responds(to:sel)
// Register https protocol
cls.perform(sel, with: "https")
URLProtocol.registerClass(Self.self)
override class func canInit(with request: URLRequest) -> Bool
return URLProtocol.property(forKey: Self.handledKey, in: request) == nil
override class func canonicalRequest(for request: URLRequest) -> URLRequest
return request
override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool
super.requestIsCacheEquivalent(a, to: b)
override func startLoading()
let redirect = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest
URLProtocol.setProperty(true, forKey: Self.handledKey, in: redirect)
sessionTask = session.dataTask(with: redirect as URLRequest)
task?.resume()
override func stopLoading()
task?.cancel()
extension MiddlewareURLProtocol : URLSessionDataDelegate
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
if let err = error
client?.urlProtocol(self, didFailWithError: err)
else
client?.urlProtocolDidFinishLoading(self)
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed)
completionHandler(.allow)
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
client?.urlProtocol(self, didLoad: data)
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void)
completionHandler(proposedResponse)
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void)
let redirect = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest
Self.removeProperty(forKey: Self.handledKey, in: redirect)
client?.urlProtocol(self, wasRedirectedTo: redirect as URLRequest, redirectResponse: response)
self.task?.cancel()
let error = NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.userCancelled.rawValue, userInfo: nil)
client?.urlProtocol(self, didFailWithError: error)
只需在应用启动时注册您的协议即可处理所有请求:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
MiddlewareURLProtocol.registerClass()
...
注意:为了防止 Apple 对私有类进行静态检查,您可以将类名存储在数组中:
let className = ["Controller", "Context", "Browsing", "WK"].reversed().joined()
【讨论】:
这个怎么用?以上是关于由于授权,WKWebview 无法加载资产文件的主要内容,如果未能解决你的问题,请参考以下文章
无法使用 WKWebView 通过 XMLHttpRequest 加载音频文件
无法仅将本地 html 文件加载到设备上的 WKWebView 中(适用于模拟器)