Android 上 Chrome 61+ 中的导航被阻止错误

Posted

技术标签:

【中文标题】Android 上 Chrome 61+ 中的导航被阻止错误【英文标题】:Navigation is blocked error in Chrome 61+ on Android 【发布时间】:2018-03-25 11:14:03 【问题描述】:

我正在构建一个登录页面,在提交和验证用户凭据后,它会打开一个本地移动应用程序。直到上周,我通过使用自定义方案 URI 让这个跨移动操作系统工作,例如:

function onLoginCallback() 
    const redirectUri = 'customscheme:/action?param1=val1&param2=val2';
    window.location.replace(redirectUri);

登录页面以 IABT 形式显示,是 In App Browser Tab 的缩写。

但是,自从 Chrome 61 版发布以来,这种方法在 android 上被打破了。 Chrome 会阻止重定向,因为没有与重定向相关的明显用户操作(有关此事的更多信息,请参阅 here)。

因此,在执行上面的代码时,我最终会在控制台中收到警告:

导航被阻止:customscheme:/action?param1=val1&param2=val2

我也尝试将自定义方案 url 更新为意图 url,但无济于事。谷歌搜索这个问题并不能轻易提供一个明确的解决方案,所以我希望任何人都可以帮助我。


编辑: 尝试使用以下场景重现问题(尽可能接近真实场景): IABT 显示一个带有单个按钮的页面 单击该按钮会触发对模拟端点的 jsonp 调用 执行 JSONP 回调并触发自定义事件 自定义事件的事件处理程序被触发并将浏览器重定向到另一个模拟端点 该模拟端点以 302 响应自定义深度链接方案。

唉,这似乎奏效了。我原以为包含 jsonp 调用会导致 Chrome 阻止最终重定向,因为它无法将其识别为用户发起的操作。


编辑 2: 设法获得可重现的场景。我们设置了一个虚拟端点,它会根据请求简单地返回一个 302,并在 Location 标头中使用自定义方案。这在所有尝试中都被阻止,除了第一个尝试。这个事实仍然令人难以置信。我们正在使用AppAuth for Android 应用程序来测试设置。

我正在为端点打开一个自定义选项卡,如下所示。代码取自this answer。

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);

【问题讨论】:

FWIW 我的问题回到了 AndroidManifest.xml 并确保标签正确。 【参考方案1】:

我们最终实现了使用classic post-redirect-get 模式的登录和注册表单。

服务器以 302 响应自定义 URI 方案。因为在此设置中,用户提交表单和接收重定向的浏览器之间没有异步执行,Chrome 正确地将操作链识别为受信任,因此不会阻止导航。

我意识到这可能不是每个人的首选解决方案。支持异步执行流程的一种可能替代方法是使用 universal links,因为它们使用常规的 http(s) 方案,Chrome 认为重定向(在发布我的问题时)不有害。

【讨论】:

当我尝试这个时,我得到一个 cors 问题,告诉我 uri 必须以 httphttps 开头。您是否能够克服这个问题并重定向到任意深度链接,例如deeplink://open @gqstav 当我们实施这个解决方案时,它也适用于自定义 URI 方案。 Android 上的 Chrome 已经更新了它们处理这些自定义方案的方式,或者您需要查看服务器上的 CORS 设置。不管怎样,我不再积极参与这个需要的项目,所以恐怕我无法提供更多帮助。【参考方案2】:

对于使用 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 );
    

这对我有用,但如果它破坏了您的授权流程,请发表评论

【讨论】:

您能解释一下为什么这可以解决问题吗? LoadingPage 方法的重定向机制与 id4 quickstart 的默认值相同。我不明白为什么 AuthorizeResponseGenerator 会改变重定向行为。为什么要设置 request.redirect? 我在这里提出了不同的解决方案 (github.com/IdentityServer/IdentityServer4/issues/5039)。据我了解,它基本上与您的效果相同,但“侵入性”较小。这和 thomax 写的一样。使用 302 重定向。

以上是关于Android 上 Chrome 61+ 中的导航被阻止错误的主要内容,如果未能解决你的问题,请参考以下文章

从 Chrome 自定义选项卡重定向到 Android 应用程序时“导航被阻止”

从 Chrome 自定义选项卡重定向到 Android 应用程序时“导航被阻止”

Chrome 61 上 webworker 中的 HTML5 安全 websocket 连接

导航栏中的 Twitter Bootstrap 搜索框在 chrome 和 firefox 中呈现不同

固定导航上的 CSS 剪辑/剪辑路径在 Chrome 和 IE 中不合作

带有IFrames Chrome 63的Android Cordova应用程序无请求标题Cookie