1、下载文件后解压到本地,其中包含一个js文件夹和放跳转的js文件,按照自己的需求上传到服务器。
如果你的网站根目录有js文件夹,那么直接把里面的js文件上传到js文件夹里,如果根目录没有js文件夹,那么连同js文件夹一起上传到网站根目录。
2、打开并编辑js文件,把白名单域名改成自己的域名,建议填www网址和手机版网址,如果不需要,把下面两行删除,//当前域名前的逗号也删除。
3、把这段js代码引入网站模板,一般是在head.html或include.html文件里,找到</script>标签,把这段代码放在</script>最上面,一定要让这个js文件最先加载,它才能有效拦截跳转。
代码全部:
// 设置允许的域名白名单(包括主域名及其子域名)
const allowedDomains = [
location.hostname, // 当前域名
'aaa', // 其他可信域名
'bbb',
'ccc'// 子域名
];
// 监听所有链接点击事件
document.addEventListener('click', function(event) {
// 向上查找最近的<a>标签
let target = event.target;
while (target && target.tagName !== 'A') {
target = target.parentElement;
if (!target) return;
}
const href = target.getAttribute('href');
// 只处理带有href属性的链接
if (!href || href.startsWith('javascript:')) return;
try {
// 解析完整URL
const url = new URL(href, document.baseURI);
// 检查域名是否在允许列表
const isAllowed = allowedDomains.some(domain =>
url.hostname === domain || url.hostname.endsWith('.' + domain)
);
// 阻止非允许域名的跳转
if (!isAllowed) {
event.preventDefault();
console.warn('禁止跳转到外部域名:', url.hostname);
alert('为了您的安全,已阻止跳转到外部网站');
// 这里可以替换为自定义处理逻辑
}
} catch (e) {
// 处理无效URL
console.error('链接解析错误:', e);
}
});
// 防止通过修改location跳转
const originalLocation = window.location;
Object.defineProperty(window, 'location', {
get: () => originalLocation,
set: value => {
try {
const url = new URL(value, document.baseURI);
if (!allowedDomains.some(d => url.hostname.endsWith(d))) {
console.warn('已阻止location跳转到:', url.hostname);
return;
}
} catch(e) {}
originalLocation.href = value;
},
configurable: false
});
或前往文章来源处下载文件获取。
根据原作者的内容我改了一份可以拦截window.open的版本,理论上会更强大。
完整代码(anti-redirect.js):
(function () {
var _original = {
open: window.open,
assign: window.location.assign.bind(window.location),
replace: window.location.replace.bind(window.location),
setTimeout: window.setTimeout.bind(window),
setInterval: window.setInterval.bind(window)
};
var _hooksInstalled = false;
var _config = null;
function init(options) {
if (_hooksInstalled) return;
_config = normalizeConfig(options);
hookWindowOpen();
hookLocationMethods();
interceptLinkClicks();
interceptFormSubmissions();
hookTimeouts();
_hooksInstalled = true;
}
function disable() {
if (!_hooksInstalled) return;
window.open = _original.open;
window.location.assign = _original.assign;
window.location.replace = _original.replace;
window.setTimeout = _original.setTimeout;
window.setInterval = _original.setInterval;
_hooksInstalled = false;
_config = null;
}
function normalizeConfig(options) {
var cfg = options || {};
var whitelist = Array.isArray(cfg.whitelist) ? cfg.whitelist.slice() : ['self'];
var allowRelative = cfg.allowRelative !== false;
var allowNonHttp = cfg.allowNonHttp !== false;
var blockProtocols = Array.isArray(cfg.blockProtocols) ? cfg.blockProtocols.map(function (p) { return String(p).toLowerCase(); }) : ['javascript', 'data'];
var onBlocked = typeof cfg.onBlocked === 'function'
? cfg.onBlocked
: function (method, url) {
console.warn('[AntiRedirect] 已阻止跳转:', method, url);
try { alert('已阻止跳转到非本站域名: ' + url); } catch (e) {}
};
return { whitelist: whitelist, allowRelative: allowRelative, allowNonHttp: allowNonHttp, blockProtocols: blockProtocols, onBlocked: onBlocked };
}
function isAllowedUrl(target) {
try {
var urlObj = target instanceof URL ? target : new URL(String(target), window.location.href);
var protocol = urlObj.protocol.toLowerCase();
var protoName = protocol.replace(':', '');
if (protoName !== 'http' && protoName !== 'https') {
if (_config.blockProtocols.indexOf(protoName) !== -1) return false;
return !!_config && _config.allowNonHttp;
}
if (!urlObj.hostname || urlObj.origin === 'null') {
return !!_config && _config.allowRelative;
}
if (urlObj.origin === window.location.origin) return true;
if (!_config || !_config.whitelist || _config.whitelist.length === 0) return false;
var host = urlObj.hostname.toLowerCase();
var origin = (urlObj.origin || (urlObj.protocol + '//' + urlObj.host)).toLowerCase();
for (var i = 0; i < _config.whitelist.length; i++) {
var rule = String(_config.whitelist[i]).toLowerCase();
if (rule === 'self') {
if (origin === window.location.origin.toLowerCase()) return true;
continue;
}
if (rule.indexOf('://') !== -1) {
if (origin === rule) return true;
continue;
}
if (rule.startsWith('*.')) {
var base = rule.slice(2);
if (host === base || host.endsWith('.' + base)) return true;
continue;
}
if (host === rule || host.endsWith('.' + rule)) return true;
}
return false;
} catch (e) {
return false;
}
}
function hookWindowOpen() {
window.open = function (url) {
if (!isAllowedUrl(url)) {
_config.onBlocked('window.open', url);
return null;
}
return _original.open.apply(window, arguments);
};
}
function hookLocationMethods() {
window.location.assign = function (url) {
if (!isAllowedUrl(url)) {
_config.onBlocked('location.assign', url);
return;
}
return _original.assign(url);
};
window.location.replace = function (url) {
if (!isAllowedUrl(url)) {
_config.onBlocked('location.replace', url);
return;
}
return _original.replace(url);
};
}
function interceptLinkClicks() {
document.addEventListener(
'click',
function (ev) {
try {
var a = ev.target && ev.target.closest ? ev.target.closest('a[href]') : null;
if (!a) return;
var href = a.href || a.getAttribute('href') || '';
if (!href) return;
if (!isAllowedUrl(href)) {
ev.preventDefault();
ev.stopPropagation();
_config.onBlocked('link.click', href);
}
} catch (e) {}
},
true
);
}
function interceptFormSubmissions() {
document.addEventListener(
'submit',
function (ev) {
try {
var form = ev.target;
if (!form || !form.action) return;
var action = form.action;
if (!isAllowedUrl(action)) {
ev.preventDefault();
ev.stopPropagation();
_config.onBlocked('form.submit', action);
}
} catch (e) {}
},
true
);
}
function hookTimeouts() {
window.setTimeout = function (handler, timeout) {
if (typeof handler === 'string') {
var url = extractUrlFromCode(handler);
if (url && !isAllowedUrl(url)) {
_config.onBlocked('setTimeout.string', url);
return 0;
}
}
return _original.setTimeout.apply(window, arguments);
};
window.setInterval = function (handler, timeout) {
if (typeof handler === 'string') {
var url = extractUrlFromCode(handler);
if (url && !isAllowedUrl(url)) {
_config.onBlocked('setInterval.string', url);
return 0;
}
}
return _original.setInterval.apply(window, arguments);
};
}
function extractUrlFromCode(code) {
try {
var patterns = [
/location\s*\.\s*href\s*=\s*['"]([^'"]+)['"]/i,
/location\s*\.\s*assign\s*\(\s*['"]([^'"]+)['"]\s*\)/i,
/location\s*\.\s*replace\s*\(\s*['"]([^'"]+)['"]\s*\)/i,
/window\s*\.\s*open\s*\(\s*['"]([^'"]+)['"]\s*,/i
];
for (var i = 0; i < patterns.length; i++) {
var m = code.match(patterns[i]);
if (m && m[1]) return m[1];
}
return null;
} catch (e) {
return null;
}
}
window.AntiRedirect = {
init: init,
disable: disable,
isAllowed: function (url) { return isAllowedUrl(url); }
};
})();
适用类型:
- 恶意跳转到其它网站
- 手机上首次访问网站时被劫持跳转到外部站点
本脚本通过拦截常见的 JS 跳转入口,阻止页面“导航到非白名单域名”,并支持白名单与协议策略配置。脚本仅对“导航行为”做域名检测,不拦截外部 JS、IMG、CSS 等资源的加载。
将js文件引入到页面的 <head> 或 <body> 使用方法和前面的版本差不多。
但需要在引入的代码下面再新加一段代码,用<script></script>包裹:
AntiRedirect.init({
// 白名单支持:'self'(同源)、纯域名、通配子域名、完整源(含协议)
whitelist: ['self', '*.example.com', 'https://trusted.partner.com'],
// 是否允许相对链接(站内链接通常是相对路径)
allowRelative: true,
// 协议策略:仅对 http/https 导航做域名检测
// 非 http/https 协议默认允许(满足“只做域名检测”),但可指定需阻止的协议
allowNonHttp: true,
blockProtocols: ['javascript', 'data'],
// 拦截回调(可选):自行处理提示或上报
onBlocked(method, url) {
console.warn('已阻止跳转', method, url);
}
});
// 需要时可调用 AntiRedirect.disable() 关闭拦截
// 也可单独使用 AntiRedirect.isAllowed(url) 做判断
- All rights reserved.
- No part of this website, including text and images, may be reproduced, modified, distributed, or transmitted in any form or by any means, without the prior written permission of the author.
- Unauthorized commercial use is strictly prohibited.
- Unauthorized personal use is strictly prohibited.
