问题
有业务人员反馈,公司的Saas平台的登录人机校验的滑动操作无法滑动。表现为:
- 测试环境公司登录时滑动组件可以滑动。
- UAT环境A公司可以滑动,B公司无法滑动。
- https环境不可以滑动,http环境可以滑动。
- 开发人员在以上相同环境都可以正常操作。
- 开发人员在任何浏览器版本都可以正常操作。
- 业务人员的chrome是最新版本。
基于以上情况,我们来的业务同事的电脑前,分别使用触摸板和鼠标操作,确实和业务人员表述的一致。
定位问题
按照经验来看,代码应该是没问题的,可能是业务同事的浏览器插件导致的,于是使用浏览器的隐私模式操作,果然操作正常了。
既然问题的方向找到了,接下来就要找到影响的插件。
分别对比了普通模式下生成的html和隐私模式下生成的袁术有什么不一样。
以下我们以某呼为例,发现滑动组件的滑块的样式被污染了

全局被添加了样式,其中关键字是#allow-copy_script
。
css
html, body, *, *::before, *::after, html body *, #allow-copy_script ~ body * {
-webkit-user-select: initial !important;
user-select: initial !important;
}
检查html中被插入了一段css代码段

解释:这段样式的代码的意思是所有的元素都强制设置用户可选中,特别是#allow-copy_script选择器后面的body下的所有元素都应用。但可以去掉这段css,依然不能滑动。
继续在页面上搜索id="allow-coyp_script"
,发现有一段script,是插件引入的。

使用新的tab页打开chrome-extension://aefehdhdciieocakfobpaaolhipkcpgc/content_scripts/copy.js,就可以看到插件的代码

其中aefehdhdciieocakfobpaaolhipkcpgc
是浏览器插件的id,可以通过id访问这个插件的设置页面chrome://extensions/?id=aefehdhdciieocakfobpaaolhipkcpgc。

禁用它,然后刷新页面,果然恢复正常了。滑动组件可以正常滑动了。
源码分析
分析下源码,是一个IIFE(立即调用函数),

尝试修改插件源码,chrome提供了Overrides功能,可以在本地映射一个文件夹,修改的源码会保存到这个本地文件夹中,使得可以修改和调试页面js、css、网络请求等。
设置Overrides本地目录

在Sources > Page中找到插件的代码,并点击鼠标右键选择覆盖内容

会自动切换到Overrides选项卡,如果你修改了内容,会有一个带颜色的点,如果保存失败,则会有一个黄色的警告图标,鼠标放上去会告诉你为啥会失败,一般是没有设置本地目录。

尝试修改下代码,在代码前添加拦截添加监听的立即执行函数,并标记下断点,刷新页面就可以进行调试。
下面是我修改的代码,目的是拦截所有注册的事件监听,拦截的事件就是插件添加的那些事件,那滑动组件使用了"mousedown", "mouseup", "mousemove"这些事件,必定会被插件拦截并阻止了事件冒泡。
typescript
(function() {
const originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
const blockedEvents = [
"copy", "cut", "contextmenu", "selectstart",
"mousedown", "mouseup", "mousemove",
"keydown", "keypress", "keyup"
];
// 插件注册这些事件时拦截掉
if (
this === document.documentElement &&
typeof listener === 'function' &&
blockedEvents.includes(type) &&
options && options.capture === true
) {
console.warn("拦截了插件尝试注册的事件监听器:", type);
return; // 阻止注册
}
// 否则放行
return originalAddEventListener.call(this, type, listener, options);
};
})();
!function() {
let e = !1;
document.addEventListener("allow_copy", (t => {
e = t.detail.unlock
}
));
const t = t => {
e && (t.stopPropagation(),
t.stopImmediatePropagation && t.stopImmediatePropagation())
}
;
["copy", "cut", "contextmenu", "selectstart", "mousedown", "mouseup", "mousemove", "keydown", "keypress", "keyup"].forEach((e => {
document.documentElement.addEventListener(e, t, {
capture: !0
})
}
))
}();
开启浏览器开发者工具-》保证插件是开启的-》修改插件源码并保存-》刷新页面这时候进入断点。并且日志打印拦截成功,滑块也可以正常滑动了。
注意,这里chrome有一个bug,仅每次修改源码的第一次刷新时或者打开开发者工具第一次刷新时,代码才会生效,所以每次刷新前都需要修改一下代码,比如给
debugger;
、debugger
删除了增加分号。或者重新打开开发者工具。 但修改网络请求和响应则是每次刷新都会生效。 以上都只在开发者工具打开的情况在生效。

解决方案
-
拦截allow_copy事件,禁止注册 ⚠️
html<script> // 阻止插件注册 allow_copy 的监听器 Object.defineProperty(document, 'addEventListener', { configurable: false, writable: false, value: new Proxy(document.addEventListener,{ apply(target, thisArg, args) { const [eventName] = args; if (eventName === 'allow_copy') { console.warn('阻止插件监听 allow_copy 事件'); return; // 阻止插件注册该事件 } return Reflect.apply(target, thisArg, args); }, }), }); </script>
该方法必须保证在插件代码执行前执行,可以添加到你页面代码的<head>
中,但如果另一个插件使用了另一个自定义事件名称enable_copy
或者其他的呢?所以这种方法不可取。

- 提醒用户
很多网站在启用了插件后,页面会被删除、添加一些元素,或者一些元素显示不正常,则会给出一个提示,让用户自主选择是否继续访问。



-
更换人机校验方案
使用点击校验、图文识别、生物校验等方案来替换滑动校验,但成本稍高、如果设计不当,会导致用户流失,且需要搭建或实现服务或接入第三方服务,既安全又准确,可以考虑,许多大公司都是采用这种方案。


-
iframe嵌入页面,iframe页面不受插件影响
成本很低,实现快速掘金、豆瓣等都是采用这种方案,在业务同事发现问题的时候,我们发现京东登录时滑块也被插件影响了,今天上去查看京东登录,京东也使用iframe嵌入滑块了。

以上就是定位了分析及解决方案的全过程。