本文记录为博客增加安全跳转页面
一、前言
今天在逛博客的时候,偶然发现一篇关于"为博客增加安全跳转中台页面"
的文章,先上原文链接给大伙瞅一瞅。
看起来还不错,而且按照文章中的思路实现也不难,唯一比较麻烦的应该是博客适配的问题。
OK ,这么简单好玩的当然得上手试一试咯。
成果展示:
二、实现分析
1. 基本思路
在开始动手之前,我们首先来理一下思路,原文中一共有两个文件,分别是 go.js
和 go.html
。
go.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| const safeGoFun = { NzcheckLink: async (domName) => { const links = document.querySelectorAll(domName); if (links) { let reg = new RegExp(/^[#\/].*/); let relative = new RegExp(/^(?!www\.|http[s]?:\/\/|[\/\\])(.*)$/); for (let i = 0; i < links.length; i++) { const ele = links[i]; let eleHref = ele.getAttribute("href"), eleIsDownLoad = ele.getAttribute("data-download"), eleRel = ele.getAttribute("rel"); if ( !reg.test(eleHref) && !relative.test(eleHref) && eleRel !== "prev" && eleRel !== "next" && eleRel !== "category" && eleRel !== "tag" && eleHref !== "javascript:void(0);" ) { if (!(await safeGoFun.NzcheckLocalSite(eleHref)) && !eleIsDownLoad) { ele.setAttribute( "href", "/go.html?goUrl=" + encodeURIComponent(eleHref) ); } else if ( !(await safeGoFun.NzcheckLocalSite(eleHref)) && eleIsDownLoad === "goDown" ) { ele.setAttribute( "href", "/go.html?goUrl=" + encodeURIComponent(eleHref) + "&type=goDown" ); } } } } }, NzcheckLocalSite: async (url) => { try { const safeUrls = ["localhost:4000", "yywen.top","gov.cn"]; let isOthers = false; for (let i = 0; i < safeUrls.length; i++) { const ele = safeUrls[i]; if (url.includes(ele)) { isOthers = true; break; } } return isOthers; } catch (err) { return true; } }, };
|
定义一个对象safeGoFun
,它里面又定义了两个函数NzcheckLink
和NzcheckLocalSite
NzcheckLink
:获取所有 a标签
,除开不需要处理再全部替换href的值NzcheckLocalSite
:校验白名单
1 2 3
| Object.keys(safeGoFun).forEach((key) => { window[key] = safeGoFun[key]; });
|
将 safeGoFun 对象的属性复制到全局的 window 对象中
1 2 3 4 5
| document.addEventListener("DOMContentLoaded", function () { window.NzcheckLink( ".post-content a:not(.social-share-icon):not(.fancybox):not(.not-check-link)" ); });
|
监听页面加载完成事件,完成时调用 NzcheckLink 函数
总的来说,就是将页面中的 a标签
的 href 属性值替换成 /go.html?goUrl=原URL
,然后我们在点击链接的时候就会先跳转到 go.html
,并将原URL当作参数传递过去。
go.html
这里没什么好说的,就是跳转显示页面,将前面传过来的 url 参数和白名单进行校对,符合就进行跳转,不符合就等用户选择
2. 差异性分析
基本情况如下:
|
博客框架 |
博客主题 |
评论区 |
博主 |
Hexo |
Volantis |
Waline |
原创 |
Hexo |
Fluid |
Waline |
可以看出就主题有所不同而已,所以实现起来应该大差不差。
不过还是需要考虑到主题所带来的问题,比如本站是启用了 PJAX
,就会产生与原代码不适配的问题。
在 PJAX 中,当使用 AJAX 加载新的页面内容时,原始页面的 JavaScript 事件处理程序不会自动绑定到新加载的内容上。因此,如果 safeGoFun 是在原始页面中绑定的事件处理程序,它不会自动应用于新加载的页面内容。
为了解决这个问题,需要在 PJAX 的回调函数中重新绑定事件处理程序,确保 safeGoFun 方法能够在新加载的内容中正确执行。
还有就是我可以通过 Volantis
主题提供的注入点(headBegin.ejs、headEnd.ejs、bodyBegin.ejs等)来编写 go.js
中的内容来达到相同的效果。其实 go.js
无论是创建在哪里,或者叫啥名都不重要,重点是在你页面加载完成之后你的 NzcheckLink
函数能运行就可以了。
三、实现过程
OK ,思路有了,那就上手试一下吧
1. 接入全局链接
经过上面的分析,基本上实现就没啥难度了,就两步搞定的事。
- 在
source/_volantis
目录下创建 bodyBegin.ejs
文件,然后将代码 copy 过了,再改一下配置即可。
source/_volantis/bodyBegin.ejs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <script> const safeGoFun = { NzcheckLink: async (domName) => { const links = document.querySelectorAll(domName); if (links) { let reg = new RegExp(/^[#\/].*/); let relative = new RegExp(/^(?!www\.|http[s]?:\/\/|[\/\\])(.*)$/); for (let i = 0; i < links.length; i++) { const ele = links[i]; let eleHref = ele.getAttribute("href"), eleIsDownLoad = ele.getAttribute("data-download"), eleRel = ele.getAttribute("rel"); if ( !reg.test(eleHref) && !relative.test(eleHref) && eleRel !== "prev" && eleRel !== "next" && eleRel !== "category" &&eleRel !== "tag" && eleHref !== "javascript:void(0);" ) { if (!(await safeGoFun.NzcheckLocalSite(eleHref))) { ele.setAttribute( "href", "/go.html?goUrl=" + encodeURIComponent(eleHref) ); } } } } }, NzcheckLocalSite: async (url) => { try { const safeUrls = ["localhost:4000", "yywen.top","gov.cn"]; let isOthers = false; for (let i = 0; i < safeUrls.length; i++) { const ele = safeUrls[i]; if (url.includes(ele)) { isOthers = true; break; } } return isOthers; } catch (err) { return true; } }, }; Object.keys(safeGoFun).forEach((key) => { window[key] = safeGoFun[key]; }); document.addEventListener("DOMContentLoaded", function () { window.NzcheckLink("#l_body a:not(.not-check-link):not(.fancybox)"); }); volantis.pjax.push(()=>{ window.NzcheckLink("#l_body a:not(.not-check-link):not(.fancybox)"); },'NzcheckLink');
</script>
|
- 在
source
目录下创建 go.html
文件,然后将代码 copy 过了,再改一下配置即可。
博主更改后的代码文件如下:
source/go.html
2. 接入评论区链接
我们先来看一下原文是怎么实现的,其代码如下:
1 2 3 4 5 6 7
| Fluid.utils.waitElementVisible('#waline .wl-cards .wl-item', () => { setTimeout(() => { window.NzcheckLink("#waline .wl-cards .wl-item a"); }, 1500); });
|
简单明了,但我们可没有 Fluid.utils.waitElementVisible
这玩意,这就需要我们自己写个 function 来处理了,不过问题不大,注意以下几个问题就🆗了:
- 文章或页面是否有开启评论区
- 评论区是否有用户发表评论
根据以上问题,博主打算采取轮询计数的方式,因为这个可以解决当页面加载完成但评论还在加载中的问题,可以适当增加轮询间隔和次数来解决评论加载时间过长的问题(比如服务部署不在国内),具体实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| volantis.pjax.push(() => { function checkElementVisibility(attempts = 0, maxAttempts = 5) { const targetElement = document.querySelector('#waline .wl-cards .wl-card-item'); const isElementVisible = targetElement && window.getComputedStyle(targetElement).display !== 'none'; if (isElementVisible) { window.NzcheckLink("#waline .wl-cards a"); } else { if (attempts < maxAttempts) { setTimeout(() => { checkElementVisibility(attempts + 1, maxAttempts); }, 2000); } } } checkElementVisibility(); }, "WacheckLink");
|
在前面bodyBegin.ejs文件中的 volantis.pjax.push
后面追加以上内容即可(64行)
最后再修改配置文件 _config.yml 跳过指定文件 go.html 的渲染
1 2
| skip_render: - 'go.html'
|
使用hexo一键三连(hexo clean && hexo g && hexo d
)就可以看到效果啦!
后续更新
Stellar主题适配
在 source 目录下创建 go.html文件
source/go.html
修改配置文件_config.yml
跳过指定文件的渲染
blog/_config.yml
1 2
| skip_render: - 'go.html'
|
使用 Stellar 主题提供的外部文件注入功能,将修改完成的 go.js
上传至 OSS/CDN 以链接注入到网站中
blog/_config.stellar.yml
1 2 3
| inject: script: - <script async src="https://dogecloud.res.yywen.top/js/stellar-safego.js"></script>
|
stellar-safego.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| console.log("安全跳转中台页面");
const safeGoFun = { NzcheckLink: async (domName) => { const links = document.querySelectorAll(domName); if (links) { let reg = new RegExp(/^[#\/].*/); let relative = new RegExp(/^(?!www\.|http[s]?:\/\/|[\/\\])(.*)$/); for (let i = 0; i < links.length; i++) { const ele = links[i]; let eleHref = ele.getAttribute("href"), eleIsDownLoad = ele.getAttribute("data-download"), eleRel = ele.getAttribute("rel"); if ( !reg.test(eleHref) && !relative.test(eleHref) && eleRel !== "prev" && eleRel !== "next" && eleRel !== "category" && eleRel !== "tag" && eleHref !== "javascript:void(0);") { if (!(await safeGoFun.NzcheckLocalSite(eleHref))) { ele.setAttribute( "href", "/go.html?goUrl=" + encodeURIComponent(eleHref) ); } } } } }, NzcheckLocalSite: async (url) => { try { const safeUrls = ["localhost:4000", "yywen.top", "gov.cn"]; let isOthers = false; for (let i = 0; i < safeUrls.length; i++) { const ele = safeUrls[i]; if (url.includes(ele)) { isOthers = true; break; } } return isOthers; } catch (err) { return true; } }, };
Object.keys(safeGoFun).forEach((key) => { window[key] = safeGoFun[key]; });
document.addEventListener("DOMContentLoaded", function () { window.NzcheckLink("#start a:not(.not-check-link):not(.fancybox)"); });
|
使用hexo一键三连(hexo clean && hexo g && hexo d
)就可以看到效果啦!
为博客增加安全跳转中台页面
转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!