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



我正在构建一个登录页面,在提交和验证用户凭据后,该页面将打开一个本地移动应用程序。直到上周,我还使用了一个自定义方案URI,类似于的跨移动操作系统

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

登录页面显示在IABT中,IABT是应用内浏览器选项卡的缩写。

然而,自从Chrome版本61发布以来,这种方法在安卓系统上被打破了。Chrome阻止重定向,因为没有明显的与重定向相关的用户操作(有关此问题的更多信息,请参阅此处)。

因此,当执行上面的代码时,我将在控制台中收到一个警告:

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

我也尝试过将自定义方案url更新为意向url,但没有成功。在谷歌上搜索这个问题并不能提供一个明确的解决方案,所以我希望上面的任何人都能帮助我。


编辑:尝试用以下场景再现问题(尽可能接近现实生活场景):

  • IABT显示带有单个按钮的页面
  • 单击该按钮将引发对mock端点的jsonp调用
  • 执行JSONP回调并触发自定义事件
  • 触发自定义事件的事件处理程序,并将浏览器重定向到另一个模拟端点
  • 该模拟端点以302响应自定义深度链接方案

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


编辑2:设法获得可复制的场景。我们已经设置了一个伪端点,该端点在请求时只需在Location标头中返回一个带有自定义方案的302。除第一次尝试外,所有尝试都会被阻止。这个事实仍然令人难以置信。我们正在使用Android应用程序的AppAuth来测试设置。

我正在打开端点的自定义选项卡,如下所示。代码取自此答案。

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

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

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

我意识到这可能不是每个人都喜欢的解决方案。支持异步执行流的一个可能的替代方案是使用通用链接,因为这些链接使用常规的http方案,Chrome(在发布我的问题时)认为重定向到该方案并不有害。

对于那些使用App Auth客户端和Identity Server的用户:

启动.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);
}
}

扩展.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 });
}

这对我有效,但如果它破坏了你的授权流中的smth,请发表评论

最新更新