浏览器失控的后退按钮?——Chrome的导航安全策略

1,422 阅读3分钟

记录一个Chrome的特性引发的问题。

背景

昨天有一个同事找我,说是页面上出现了非常奇怪的跳转现象。

简单来说就是,他做了一个前置页面A,用于获取用户登录信息,根据用户角色的不同,自动跳转至不同路由的页面。但是当他点击浏览器后退时,返回的不是页面A,而是空白的新标签页。相当于页面A的历史记录被浏览器吃掉了,所以直接后退到了新标签页。

我之前遇到过浏览器导航后退出现了非预期的情况,但只是出现在UC浏览器,并且是在 pathname 路由的模式下。但同事给我发的视频中,使用的是Chrome,且用的 hash 路由。之前的经验让我第一时间联想到,是不是Chrome出现新bug了?

测试

我写了一个简单的可复现用例

// PageA.tsx
import React, { useEffect } from 'react';
import { history } from 'umi';

const PageA = () => {
  useEffect(() => {
    setTimeout(() => {
      history.push('/sub-page/1');
      // window.history.pushState(null, '', '/sub-page/1');
      // location.href = '/sub-page/1';
    }, 1000);
  }, []);
  return (
    <div>
      This is Page A.
    </div>
  );
};

export default PageA;

然后我得到了这样一个稳定的现象,页面A在1秒后跳转至页面B,但点击后退按钮时,并不能回退到页面A,且不论是使用react 御用的 history或原生history或是直接修改location,都能稳定复现这个情况。

navi1.gif

然后我也发现了能够避免吃掉历史记录的方式,就是用户与页面产生交互,比如这样点击一下页面,前进后退就正常了:

navi2.gif

emmm?“用户交互后才生效”这个操作怎么和之前 Chrome 提出的,禁止在用户与页面交互前自动播放有声视频的安全策略如出一辙?我隐约感觉到这可能不是bug

排查

由于我先入为主的观点,我还是以为这是Chrome的缺陷,也就是我认为,在用户与页面交互之前,浏览器遗漏了历史记录,所以我在chromiumissue看板上搜了好一会儿,但是并没有得到我想要的结果。

我不死心,拉了历史版本的Chromium挨个测试,最后发现在74版本的Chromium上,出现了这个特性。这个版本距离现在已经两年了吧,这么明显的问题不可能没有人提出吧?

于是我开始往浏览器新特性的方向去查找,查了74版本相关的New in Chrome,但似乎没有找到太多内容。

就在这时,同事给我发来了一个链接,这里说明了在用户与页面交互之前,Chrome的前进后退按钮会忽略该网站所跳转过的历史记录栈。

这个操作是为了防止网站恶意拦截用户的浏览器导航而做的。例如该网站通过在页面A进入页面后立即push跳转到新页面的代码,导致你后退时便触发该跳转,而永远无法后退到更早的页面。

后来我也找到了相关的文章,看来之前的方向不太对。

结语

由于存在这个特性,作为前端工程师的我们,在页面跳转时就要尽量避免这种,用户进入页面后直接跳转页面,且还需要用户进行后退操作的业务逻辑。

另外时间关系,我只搜索到了上面两个博主发的介绍文章,如果有官方对于该特性的文档,欢迎告知。