ios - 由于授权,WKWebview 无法加载 Assets 文件

标签 ios swift amazon-cognito wkwebview

我有一个应用 AWS Cognito 的 WKWebview。
对服务器的每个请求都必须在请求 header 中添加授权。

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?
感谢您的帮助。

最佳答案

您可以将所有 webview 的请求重定向到您的 URLSession与您的配置。为此,您可以注册您的自定义 URLProtocol对于 https方案。 WKWebView 有一个 hack使用 WKBrowsingContextController 拦截 url 请求私有(private)类(class)和您的URLProtocol实现例如:

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)
    }
}
只需在 app start 上注册您的协议(protocol)即可处理所有请求:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    MiddlewareURLProtocol.registerClass()
    ...
}
注意:为了防止 Apple 对私有(private)类进行静态检查,您可以将类名存储在数组中:
let className = ["Controller", "Context", "Browsing", "WK"].reversed().joined()

关于ios - 由于授权,WKWebview 无法加载 Assets 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63656053/

相关文章:

c++ - 错误 Xcode 5.1 : unknown argument: '-cclib' [-Wunused-command-line-argument-hard-error-in-future]

ios - 以灰度渲染下面的所有 UIView

ios - scrollViewDidScroll : on UITableViewRowAnimation?

amazon-web-services - AWS Cognito 身份池配置无效

ios - Swift UITextField adjustmentFontSizeToFitWidth 无法正常工作

swift - UIImageView 的延迟时间

ios - 我从哪里得到 "valid Oauth 2.0 token for the service account of the Firebase project"

swift - 尝试在 Swift 上使用自定义 UITextField 时出现 exc_bad_access 错误

amazon-web-services - Amazon Cognito为什么不在其访问 token 中返回受众字段?

amazon-web-services - 验证电子邮件是否适用于 AWS Cognito 每日电子邮件配额?