Chrome 61+ Android 中的导航被阻止错误
Navigation is blocked error in Chrome 61+ on Android
我正在构建一个登录页面,在提交和验证用户凭据后,该页面会打开一个本机移动应用程序。直到上周,我通过使用自定义方案 URI 实现了跨移动设备 OS,例如:
function onLoginCallback() {
const redirectUri = 'customscheme:/action?param1=val1¶m2=val2';
window.location.replace(redirectUri);
}
登录页面显示在 IABT 中,是 In App Browser Tab 的缩写。
但是,自从 Chrome 的第 61 版发布以来,这种方法在 Android 上被打破了。 Chrome 阻止重定向,因为没有与重定向相关的明显用户操作(有关此事的更多信息,请参阅 here)。
因此,当执行上面的代码时,我最终会在控制台中收到警告:
Navigation is blocked: customscheme:/action?param1=val1¶m2=val2
我也尝试将自定义方案 url 更新为意图 url 但无济于事。谷歌搜索这个问题并不能轻易提供明确的解决方案,所以我希望任何人都可以帮助我。
编辑: 尝试使用以下场景重现问题(尽可能接近真实场景):
- IABT 显示带有单个按钮的页面
- 单击该按钮会触发对模拟端点的 jsonp 调用
- 执行 JSONP 回调并触发自定义事件
- 触发自定义事件的事件处理程序并将浏览器重定向到另一个模拟端点
- 该模拟端点以 302 响应自定义深层链接方案。
唉,这似乎奏效了。我原以为包含 jsonp 调用会导致 Chrome 阻止最终重定向,因为它无法将其识别为用户发起的操作。
编辑 2: 设法获得可重现的场景。我们已经设置了一个虚拟端点,根据请求只需 returns 一个 302
和位置 header 中的自定义方案。除第一个尝试外,所有尝试均被阻止。这个事实至今仍令人费解。我们正在使用 AppAuth for Android 应用程序来测试设置。
我正在打开端点的自定义选项卡,如下所示。代码取自.
void launchTab(Context context, Uri uri){
final CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient client) {
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
final CustomTabsIntent intent = builder.build();
client.warmup(0L); // This prevents backgrounding after redirection
intent.launchUrl(context, uri);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
CustomTabsClient.bindCustomTabsService(context, "com.android.chrome", connection);
}
我们最终使用 classic post-redirect-get 模式实现了我们的登录和注册表单。
服务器用 302
响应自定义 URI 方案。因为在此设置中,在用户提交表单和浏览器接收重定向之间没有异步执行,Chrome 正确地将操作链识别为可信的,因此不会阻止导航。
我意识到这可能不是每个人的首选解决方案。支持异步执行流程的一种可能替代方法是使用 universal links,因为它们使用常规的 http(s) 方案,Chrome 认为重定向(在发布我的问题时)不有害。
对于使用 App Auth 客户端和 Identity Server 的用户:
Startup.cs
services.AddTransient<IAuthorizeResponseGenerator, AuthorizeRG>();
AuthorizeRG.cs
public class AuthorizeRG: AuthorizeResponseGenerator
{
public override async Task<AuthorizeResponse> CreateResponseAsync(ValidatedAuthorizeRequest request)
{
var response = await base.CreateResponseAsync(request);
if (response.RedirectUri != null && request.IsNativeClient())
//this fix chrome navigation blocked on native clients https://bugs.chromium.org/p/chromium/issues/detail?id=738724
response.Request.RedirectUri = $"/native/redirect/{HttpUtility.UrlEncode(response.RedirectUri)}";
return response;
}
}
NativeController.cs
[Route("[controller]")]
public class NativeController : Controller
{
[HttpGet("Redirect/{redirectUri}")]
public IActionResult Redirect([FromRoute] string redirectUri)
{
redirectUri = HttpUtility.UrlDecode(redirectUri);
redirectUri += HttpContext.Request.QueryString.ToUriComponent();
return this.LoadingPage("Redirect", redirectUri);
}
}
Extensions.cs
/// <summary>
/// Checks if the redirect URI is for a native client.
/// </summary>
/// <returns></returns>
public static bool IsNativeClient(this AuthorizationRequest context)
{
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
}
public static bool IsNativeClient(this ValidatedAuthorizeRequest context)
{
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
}
public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri)
{
controller.HttpContext.Response.StatusCode = 200;
controller.HttpContext.Response.Headers["Location"] = "";
return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri });
}
这对我有用,但如果它破坏了您的授权流程,请发表评论
我正在构建一个登录页面,在提交和验证用户凭据后,该页面会打开一个本机移动应用程序。直到上周,我通过使用自定义方案 URI 实现了跨移动设备 OS,例如:
function onLoginCallback() {
const redirectUri = 'customscheme:/action?param1=val1¶m2=val2';
window.location.replace(redirectUri);
}
登录页面显示在 IABT 中,是 In App Browser Tab 的缩写。
但是,自从 Chrome 的第 61 版发布以来,这种方法在 Android 上被打破了。 Chrome 阻止重定向,因为没有与重定向相关的明显用户操作(有关此事的更多信息,请参阅 here)。
因此,当执行上面的代码时,我最终会在控制台中收到警告:
Navigation is blocked: customscheme:/action?param1=val1¶m2=val2
我也尝试将自定义方案 url 更新为意图 url 但无济于事。谷歌搜索这个问题并不能轻易提供明确的解决方案,所以我希望任何人都可以帮助我。
编辑: 尝试使用以下场景重现问题(尽可能接近真实场景):
- IABT 显示带有单个按钮的页面
- 单击该按钮会触发对模拟端点的 jsonp 调用
- 执行 JSONP 回调并触发自定义事件
- 触发自定义事件的事件处理程序并将浏览器重定向到另一个模拟端点
- 该模拟端点以 302 响应自定义深层链接方案。
唉,这似乎奏效了。我原以为包含 jsonp 调用会导致 Chrome 阻止最终重定向,因为它无法将其识别为用户发起的操作。
编辑 2: 设法获得可重现的场景。我们已经设置了一个虚拟端点,根据请求只需 returns 一个
302
和位置 header 中的自定义方案。除第一个尝试外,所有尝试均被阻止。这个事实至今仍令人费解。我们正在使用 AppAuth for Android 应用程序来测试设置。
我正在打开端点的自定义选项卡,如下所示。代码取自
void launchTab(Context context, Uri uri){
final CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient client) {
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
final CustomTabsIntent intent = builder.build();
client.warmup(0L); // This prevents backgrounding after redirection
intent.launchUrl(context, uri);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
CustomTabsClient.bindCustomTabsService(context, "com.android.chrome", connection);
}
我们最终使用 classic post-redirect-get 模式实现了我们的登录和注册表单。
服务器用 302
响应自定义 URI 方案。因为在此设置中,在用户提交表单和浏览器接收重定向之间没有异步执行,Chrome 正确地将操作链识别为可信的,因此不会阻止导航。
我意识到这可能不是每个人的首选解决方案。支持异步执行流程的一种可能替代方法是使用 universal links,因为它们使用常规的 http(s) 方案,Chrome 认为重定向(在发布我的问题时)不有害。
对于使用 App Auth 客户端和 Identity Server 的用户:
Startup.cs
services.AddTransient<IAuthorizeResponseGenerator, AuthorizeRG>();
AuthorizeRG.cs
public class AuthorizeRG: AuthorizeResponseGenerator
{
public override async Task<AuthorizeResponse> CreateResponseAsync(ValidatedAuthorizeRequest request)
{
var response = await base.CreateResponseAsync(request);
if (response.RedirectUri != null && request.IsNativeClient())
//this fix chrome navigation blocked on native clients https://bugs.chromium.org/p/chromium/issues/detail?id=738724
response.Request.RedirectUri = $"/native/redirect/{HttpUtility.UrlEncode(response.RedirectUri)}";
return response;
}
}
NativeController.cs
[Route("[controller]")]
public class NativeController : Controller
{
[HttpGet("Redirect/{redirectUri}")]
public IActionResult Redirect([FromRoute] string redirectUri)
{
redirectUri = HttpUtility.UrlDecode(redirectUri);
redirectUri += HttpContext.Request.QueryString.ToUriComponent();
return this.LoadingPage("Redirect", redirectUri);
}
}
Extensions.cs
/// <summary>
/// Checks if the redirect URI is for a native client.
/// </summary>
/// <returns></returns>
public static bool IsNativeClient(this AuthorizationRequest context)
{
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
}
public static bool IsNativeClient(this ValidatedAuthorizeRequest context)
{
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
}
public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri)
{
controller.HttpContext.Response.StatusCode = 200;
controller.HttpContext.Response.Headers["Location"] = "";
return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri });
}
这对我有用,但如果它破坏了您的授权流程,请发表评论