Android 是否允许本机应用程序针对 http://(非本地/文件)请求禁用 CORS 安全策略?
在我的本机应用程序中,Web 视图通过 http:// 显示远程 html,而不是在本地/文件系统上。这似乎与网络浏览器中一样受到 CORS 限制。
Worakround:用于向没有
Access-Control-Allow-Origin: *
的跨域 ajax 请求的本机 js 桥是我的快速解决方案。 (jsonp 或服务器端代理不是一个选项,因为客户端的 cookie+ip 由 Web 服务检查。)
可以针对应用内 Web 视图禁用此策略吗?
如果有一个简单的标志允许 js 绕过此限制(限制“本机”应用程序的 Web 视图),请告诉我。
从 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 后可用。
据我所知这是不可能的,相信我,我已经尝试了很多方法。
您能做的最好的事情就是覆盖资源加载。请参阅拦截并覆盖来自 WebView 的 HTTP 请求
我已经成功一半了。唯一缺少的是 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
}
}
帮助我在 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 策略阻止的。