由于授权,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 方案注册您的自定义URLProtocolWKWebView 有一个 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 中(适用于模拟器)

WKWebView 无法在 HTML 中加载本地资源

OSX Sandbox WKWebView 在里面加载本地文件

无法将文本从 WKWebview 复制到剪贴板

无法加载资产颤振(不是资产是保存的文件)