前言
网页的安全性优化是一个越来越被开发者重视的问题(例如配置 SSL
证书实现网站的 https
访问,通过插件对 XSS
攻击进行预防等),今天我们就来分析 掘金
是如何实现网站跳转外部链接时“拦截”并通知的功能。
一、业务场景
在 掘金
的文章或者沸点中打开一个链接时,如果为 外部链接
(非本站的链接)时,则会先跳转至一个风险提示的页面,告知用户即将打开的新页面 出现任何问题概不负责 非本站提供,注意账号财产安全。其实就是一个免责的声明。(以下将用一篇文章中的某个外部链接做演示。因为这个网站的加载动画非常好康)
其中最主要的提示页面如下图:
二、代码分析
1. 链接元素
我们需要对其代码(可见的)进行分析,首先关注点击跳转的元素。打开控制台 检查元素
查看此链接元素有什么特殊的地方。
通过上图我们可以直观看出链接前被拼接了掘金内部链接 https://link.juejin.cn/?target=
,而 =
后的网址才是真实网址。
2. 风险提示
然后我们再来看下风险提示页。
在 风险提示
页,只需将 URL
上的 target
参数取出并显示在页面中即可。(URL传参常见于各类搜索页面)
3. 链接处理
当然还有关键的一环是,这个链接是何时被处理成这样的。先来看看是不是在编辑文章时处理的。
从上图可知,编辑文章时链接并未实时做处理。我们知道掘金的文章草稿是实时保存的,那么我们来看下请求中发送的数据是否做了处理。
从发送的请求中可以看出字段 mark_content
中的链接并未作处理,则链接应该是在后台被处理的。
三、进行编码
1. 风险提示页面
掘金的技术栈是 Vue2.x
,此处给出 Vue2.x
版本的写法:
// 风险提示页
<template>
<div class="box">
<div class="tip-box">
<img
class="logo"
src="https://lf-cdn-tos.bytescm.com/obj/static/link_juejin_cn/assets/logo_new.0ec938fb.svg"
/>
<div class="content">
<div class="title">
<del>冰冰你个小可爱</del> 即将离开稀土掘金,请注意账号财产安全
</div>
<div class="link">{
{ target }}</div>
<button class="btn" @click="navigateToTarget">继续访问</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: "RiskTip",
data() {
return { target: "" };
},
methods: {
// 获取 url
getTargetURL() {
const query = window.location.href.split("?")[1] || "";
const target = query.split("target=")[1] || "";
this.target = window.decodeURIComponent(target);
},
// 跳转页面
navigateToTarget() {
if (!this.target) {
return;
}
window.location.href = this.target;
},
},
mounted() {
this.getTargetURL(); // 获取 url
},
};
</script>
<style scoped>
.box {
height: 100vh;
background-color: #f4f5f5;
}
.box .tip-box {
position: absolute;
left: 50%;
top: 30%;
max-width: 624px;
width: 86%;
background-color: #fff;
transform: translateX(-50%);
padding: 30px 40px 0;
box-sizing: border-box;
border: 1px solid #e5e6eb;
border-radius: 2px;
}
.box .tip-box .logo {
display: block;
width: 116px;
height: 24px;
position: absolute;
top: -40px;
left: 0;
}
.box .tip-box .content .title {
font-size: 18px;
line-height: 24px;
}
.box .tip-box .content .link {
padding: 16px 0 24px;
border-bottom: 1px solid #e5e6eb;
position: relative;
color: gray;
font-size: 14px;
}
.box .tip-box .content .btn {
display: block;
margin: 20px 0 24px auto;
color: #fff;
border-radius: 3px;
border: none;
background: #007fff;
height: 32px;
font-size: 14px;
padding: 0 14px;
cursor: pointer;
outline: 0;
}
</style>
让我们来康康效果如何:
2. 前端链接处理
由请求传参可知,富文本中的链接并未作处理,那如果我们需要前端做处理然后再返回给后台的话,就需要借助正则表达式,替换所有富文本中的链接。这里我们封装一个公共方法:
// utils/index.js
/**
* 替换富文本中的链接
* 将富文本中的不是本站的链接前追加 "https://link.juejin.cn/?target="
* @param html 传入富文本
* @returns html 处理后的富文本
*/
export const replaceHTMLHref = (html) => {
const juejinReg = /https:\/\/juejin.cn/g; // 匹配"https://juejin.cn"
const juejinSignReg = /JUEJIN_URL/g; // 匹配"JUEJIN_URL"
const urlReg =
/((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*(?!juejin.cn)[\w\-\@?^=%&/~\+#])?)/g; // 匹配所有网址
/**
* 匹配替换三次
* 第一次将所有"https://juejin.cn"替换为"JUEJIN_URL"
* 第二次将所有网址增加前缀"https://link.juejin.cn/?target="
* 第三次将所有"JUEJIN_URL"替换为"https://juejin.cn"
*/
const result = html
.replace(juejinReg, "JUEJIN_URL")
.replace(urlReg, "https://link.juejin.cn/?target=$1")
.replace(juejinSignReg, "https://juejin.cn");
return result;
};
我们写一段代码测试一下,康康他是否生效:
// 省略其他非关键代码
import {
replaceHTMLHref } from "@/utils";
const str = '一个外部网址:\nhttps://webgradients.com/\n又一个外部网址:\nhttps://www.baidu.com/\n掘金:\nhttps://juejin.cn/';
console.log(replaceHTMLHref(str));
至此,前端处理富文本中的链接的功能已实现。
四、掘金彩蛋
在查看编辑文章请求的时候偶然发现,编辑文章页面的控制台会打印如下内容:
控制台彩蛋很多网站都有,但不得不说,掘金官方还真是蛮可爱的。٩(‘ω’)و
小结
通过检查元素、查看请求等方式,推导了掘金「跳转外链风险提示」的可能实现方式。但相信我们从此案例中收获到的不仅仅是一种解决方案,更是一种处理问题的思路。我们只有不断向优秀的项目学习,才能更好的提升自己的技术水平。