Android Web 视图:禁用 CORS

问题描述 投票:0回答:4

Android 是否允许本机应用程序针对 http://(非本地/文件)请求禁用 CORS 安全策略?

在我的本机应用程序中,Web 视图通过 http:// 显示远程 html,而不是在本地/文件系统上。这似乎与网络浏览器中一样受到 CORS 限制。

Worakround:用于向没有

Access-Control-Allow-Origin: *
的跨域 ajax 请求的本机 js 桥是我的快速解决方案。 (jsonp 或服务器端代理不是一个选项,因为客户端的 cookie+ip 由 Web 服务检查。)

可以针对应用内 Web 视图禁用此策略吗?

如果有一个简单的标志允许 js 绕过此限制(限制“本机”应用程序的 Web 视图),请告诉我。

android webview cors
4个回答
21
投票

从 Android API 级别 21 开始,这已经成为可能。您可以像这样创建 OPTIONS 响应:

public class OptionsAllowResponse {
    static final SimpleDateFormat formatter = new SimpleDateFormat("E, dd MMM yyyy kk:mm:ss", Locale.US);

    @TargetApi(21)
    static WebResourceResponse build() {
        Date date = new Date();
        final String dateString = formatter.format(date);

        Map<String, String> headers = new HashMap<String, String>() {{
            put("Connection", "close");
            put("Content-Type", "text/plain");
            put("Date", dateString + " GMT");
            put("Access-Control-Allow-Origin", /* your domain here */);
            put("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS");
            put("Access-Control-Max-Age", "600");
            put("Access-Control-Allow-Credentials", "true");
            put("Access-Control-Allow-Headers", "accept, authorization, Content-Type");
            put("Via", "1.1 vegur");
        }};

        return new WebResourceResponse("text/plain", "UTF-8", 200, "OK", headers, null);
    }
}

然后从 WebViewClient 实现中调用它,如下所示:

    @Override
    @TargetApi(21)
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        if (request.getMethod().equalsIgnoreCase("OPTIONS")) {
            return OptionsAllowResponse.build();
        }

        return null;
    }

这仅适用于 API 级别 21,因为 OPTIONS 响应需要检查 WebResourceRequest 中请求的 HTTP 方法,而 WebResourceRequest 仅在 API 21 后可用。


12
投票

据我所知这是不可能的,相信我,我已经尝试了很多方法。

您能做的最好的事情就是覆盖资源加载。请参阅拦截并覆盖来自 WebView 的 HTTP 请求


0
投票

我已经成功一半了。唯一缺少的是 POST 和 PUT 请求,它们需要的不仅仅是解决选项请求:

fun interceptWebRequest(request: WebResourceRequest?): WebResourceResponse? {
    try {
        if (request == null || request.isForMainFrame) {
            return null
        }

        if (request.url?.path?.contains("assets/icons/favicon") == true) {
            try {
                return WebResourceResponse("image/png", null, null)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

        if (request.url.toString().contains(serviceHost)) {
            return null
        }

        Log.v(
            "TW",
            "interceptRequest mf:${request?.isForMainFrame.toString()} ${request.method} ${request?.url}"
        )

        // since we currently don't have a way to also post the body, we only handle GET, HEAD and OPTIONS requests
        // see https://github.com/KonstantinSchubert/request_data_webviewclient for a possible solution
        if (request.method.uppercase() != "GET" && request.method.uppercase() != "OPTIONS" && request.method.uppercase() != "HEAD") {
            return null
        }

        val client = OkHttpClient()
        val newRequestBuilder = Request.Builder()
            .url(request.url.toString())
            .method(request.method, null)

        for ((key, value) in request.requestHeaders) {
            Log.v("TW", "interceptRequest header:${key} – ${value}")
            if (key == "User-Agent" || key == "Origin" || key == "Referer" || key == "Sec-Fetch-Mode") {
                continue
            }
            newRequestBuilder.addHeader(key, value)
        }

        newRequestBuilder.header("User-Agent", "curl/7.64.1")
        val newRequest = newRequestBuilder.build()

        if (request.method.uppercase() == "OPTIONS") {
            Log.v("TW", "OPTIONS request triggered")
            return OptionsAllowResponse.build()
    
        }
        //-------------


        Log.v("TW", "exec request ${request.url}")
        client.newCall(newRequest).execute().use { response ->
            Log.v("TW", "response ${response.code} ${response.message}")
            val responseHeaders = response.headers.names()
                .associateWith { response.headers(it)?.joinToString() }
                .toMutableMap()

            upsertKeyValue(responseHeaders, "Access-Control-Allow-Origin", "*")
            upsertKeyValue(
                responseHeaders,
                "Access-Control-Allow-Methods",
                "GET, POST, OPTIONS"
            )

            val contentType = response.header("Content-Type", "text/plain")
            val contentEncoding = response.header("Content-Encoding", "utf-8")

            val inputStream = ByteArrayInputStream(response.body?.bytes())
            val reasonPhrase =
                response.message.ifEmpty { "OK" } // provide a default value if the message is null or empty

            return WebResourceResponse(
                contentType,
                contentEncoding,
                response.code,
                reasonPhrase,
                responseHeaders,
                inputStream
            )
        }
    } catch (e: Exception) {
        Log.e("WebViewRequestHandler", "Error in interceptWebRequest => Not intercepting", e)
        return null
    }
}

-2
投票

帮助我在 Xamarin 中使用 C# 的较短版本如下:

    public override WebResourceResponse ShouldInterceptRequest(WebView view, IWebResourceRequest request)
    {
        if (request.Url.SchemeSpecificPart.StartsWith("//<domain>/"))
        {
            request.RequestHeaders.Add("Access-Control-Allow-Origin", "*");
        }

        return base.ShouldInterceptRequest(view, request);
    }

此覆盖是针对

Android.Webkit.WebViewClient
完成的,其中
<domain>
是被 CORS 策略阻止的。

© www.soinside.com 2019 - 2024. All rights reserved.