著 Chrome 77 的發(fā)布,谷歌也為 Android 和桌面版本引入了全新的站點(diǎn)隔離安全特性。此前,當(dāng)熔毀(Meltdown)和幽靈(Spectre)漏洞被披露的時(shí)候,谷歌就快速推出了這項(xiàng)安全特性,以便 Chrome 67 用戶(hù)啟用。開(kāi)啟之后,Chrome 瀏覽器將把用戶(hù)訪問(wèn)的每個(gè)網(wǎng)站,都加載到單獨(dú)的沙盒進(jìn)程中,以限制其對(duì)資源和功能的訪問(wèn)。
(題圖 via Bleeping Computer)
通過(guò)這種方式,能夠有效地防止惡意網(wǎng)站利用推測(cè)執(zhí)行攻擊漏洞,來(lái)訪問(wèn)加載在其它瀏覽器選項(xiàng)卡中的數(shù)據(jù)。
然而啟用站點(diǎn)隔離的代價(jià),就是耗費(fèi)更多的進(jìn)程和內(nèi)存資源,導(dǎo)致某些網(wǎng)站的內(nèi)存用量爆炸。但在安全性和資源利用率之間,總要作出一定的權(quán)衡。
隨著 Android 版 CChrome 77 的發(fā)布,谷歌決定進(jìn)一步增強(qiáng)對(duì)移動(dòng)用戶(hù)的防護(hù),所以引入了與桌面版本略有不同的網(wǎng)站隔離措施。
據(jù)悉,Android 版“網(wǎng)站隔離”特性,僅會(huì)保護(hù)用戶(hù)通過(guò)密碼登陸的站點(diǎn),以減少移動(dòng)設(shè)備的資源占用率,畢竟其處理器和內(nèi)存性能都低于臺(tái)式機(jī)計(jì)算機(jī)。
谷歌表示,目前已有 99% 運(yùn)行 Android 且內(nèi)存超過(guò) 2GB 的用戶(hù)啟用了此功能,且有 1% 用戶(hù)保留了監(jiān)測(cè)功能以提升性能。在未來(lái),谷歌還考慮向更多設(shè)備提供支持。
對(duì)于想要提供完整站點(diǎn)隔離保護(hù)功能的用戶(hù),可在地址欄輸入 chrome:// flags/#enable-site-per-process 并跳轉(zhuǎn),然后啟用這個(gè)標(biāo)記。
至于臺(tái)式機(jī)用戶(hù),目前 Chrome 77 的“站點(diǎn)隔離”功能亦可保護(hù)用戶(hù)免受渲染器進(jìn)程的影響。這些進(jìn)程負(fù)責(zé)各個(gè)標(biāo)簽頁(yè)中發(fā)生的事情,例如將 HTML、CSS 和 JavaScript 代碼轉(zhuǎn)義為網(wǎng)頁(yè)并顯示。
一些攻擊者試圖對(duì)其它標(biāo)簽頁(yè)中的網(wǎng)頁(yè)代碼展開(kāi)攻擊,但新加入的隔離措施,可有效防止此類(lèi)惡意活動(dòng)的發(fā)生。
者:HelloGitHub-追夢(mèng)人物
文中涉及的示例代碼,已同步更新到HelloGitHub-Team 倉(cāng)庫(kù)[1]
上一篇中我們使用了 Markdown 來(lái)為文章提供排版支持。Markdown 在解析內(nèi)容的同時(shí)還可以自動(dòng)提取整個(gè)內(nèi)容的目錄結(jié)構(gòu),現(xiàn)在我們來(lái)使用 Markdown 為文章自動(dòng)生成目錄。
先來(lái)回顧一下博客的 Post(文章)模型,其中 body 是我們存儲(chǔ) Markdown 文本的字段:
blog/models.py from django.db import models class Post(models.Model): # Other fields ... body = models.TextField()
再來(lái)回顧一下文章詳情頁(yè)的視圖,我們?cè)?detail 視圖函數(shù)中將 post 的 body 字段中的 Markdown 文本解析成了 HTML 文本,然后傳遞給模板顯示。
blog/views.py def detail(request, pk): post = get_object_or_404(Post, pk=pk) post.body = markdown.markdown(post.body, extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) return render(request, 'blog/detail.html', context={'post': post})
markdown.markdown() 方法把 post.body 中的 Markdown 文本解析成了 HTML 文本。同時(shí)我們還給該方法提供了一個(gè) extensions 的額外參數(shù)。其中 markdown.extensions.toc 就是自動(dòng)生成目錄的拓展(這里可以看出我們有先見(jiàn)之明,如果你之前沒(méi)有添加的話記得現(xiàn)在添加進(jìn)去)。
在渲染 Markdown 文本時(shí)加入了 toc 拓展后,就可以在文中插入目錄了。方法是在書(shū)寫(xiě) Markdown 文本時(shí),在你想生成目錄的地方插入 [TOC] 標(biāo)記即可。例如新寫(xiě)一篇 Markdown 博文,其 Markdown 文本內(nèi)容如下:
[TOC] ## 我是標(biāo)題一 這是標(biāo)題一下的正文 ## 我是標(biāo)題二 這是標(biāo)題二下的正文 ### 我是標(biāo)題二下的子標(biāo)題 這是標(biāo)題二下的子標(biāo)題的正文 ## 我是標(biāo)題三 這是標(biāo)題三下的正文
其最終解析后的效果就是:
原本 [TOC] 標(biāo)記的地方被內(nèi)容的目錄替換了。
上述方式的一個(gè)局限性就是只能通過(guò) [TOC] 標(biāo)記在文章內(nèi)容中插入目錄。如果我想在頁(yè)面的其它地方,比如側(cè)邊欄插入一個(gè)目錄該怎么做呢?方法其實(shí)也很簡(jiǎn)單,只需要稍微改動(dòng)一下解析 Markdown 文本內(nèi)容的方式即可,具體代碼就像這樣:
blog/views.py def detail(request, pk): post = get_object_or_404(Post, pk=pk) md = markdown.Markdown(extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) post.body = md.convert(post.body) post.toc = md.toc return render(request, 'blog/detail.html', context={'post': post})
和之前的代碼不同,我們沒(méi)有直接用 markdown.markdown() 方法來(lái)渲染 post.body 中的內(nèi)容,而是先實(shí)例化了一個(gè) markdown.Markdown 對(duì)象 md,和 markdown.markdown() 方法一樣,也傳入了 extensions 參數(shù)。接著我們便使用該實(shí)例的 convert 方法將 post.body 中的 Markdown 文本解析成 HTML 文本。而一旦調(diào)用該方法后,實(shí)例 md 就會(huì)多出一個(gè) toc 屬性,這個(gè)屬性的值就是內(nèi)容的目錄,我們把 md.toc 的值賦給 post.toc 屬性(要注意這個(gè) post 實(shí)例本身是沒(méi)有 toc 屬性的,我們給它動(dòng)態(tài)添加了 toc 屬性,這就是 Python 動(dòng)態(tài)語(yǔ)言的好處)。
接下來(lái)就在博客文章詳情頁(yè)的文章目錄側(cè)邊欄渲染文章的目錄吧!刪掉占位用的目錄內(nèi)容,替換成如下代碼:
{% block toc %} <div class="widget widget-content"> <h3 class="widget-title">文章目錄</h3> {{ post.toc|safe }} </div> {% endblock toc %}
即使用模板變量標(biāo)簽 {{ post.toc }} 顯示模板變量的值,注意 post.toc 實(shí)際是一段 HTML 代碼,我們知道 django 會(huì)對(duì)模板中的 HTML 代碼進(jìn)行轉(zhuǎn)義,所以要使用 safe 標(biāo)簽防止 django 對(duì)其轉(zhuǎn)義。其最終渲染后的效果就是:
現(xiàn)在目錄已經(jīng)可以完美生成了,不過(guò)還有一個(gè)異常情況,當(dāng)文章沒(méi)有任何標(biāo)題元素時(shí),Markdown 就提取不出目錄結(jié)構(gòu),post.toc 就是一個(gè)空的 div 標(biāo)簽,如下:
<div class="toc"> <ul></ul> </div>
對(duì)于這種沒(méi)有目錄結(jié)構(gòu)的文章,在側(cè)邊欄顯示一個(gè)目錄是沒(méi)有意義的,所以我們希望只有在文章存在目錄結(jié)構(gòu)時(shí),才顯示側(cè)邊欄的目錄。那么應(yīng)該怎么做呢?
分析 toc 的內(nèi)容,如果有目錄結(jié)構(gòu),ul 標(biāo)簽中就有值,否則就沒(méi)有值。我們可以使用正則表達(dá)式來(lái)測(cè)試 ul 標(biāo)簽中是否包裹有元素來(lái)確定是否存在目錄。
def detail(request, pk): post = get_object_or_404(Post, pk=pk) md = markdown.Markdown(extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) post.body = md.convert(post.body) m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S) post.toc = m.group(1) if m is not None else '' return render(request, 'blog/detail.html', context={'post': post})
這里我們正則表達(dá)式去匹配生成的目錄中包裹在 ul 標(biāo)簽中的內(nèi)容,如果不為空,說(shuō)明目錄,就把 ul 標(biāo)簽中的值提取出來(lái)(目的是只要包含目錄內(nèi)容的最核心部分,多余的 HTML 標(biāo)簽結(jié)構(gòu)丟掉)賦值給 post.toc;否則,將 post 的 toc 置為空字符串,然后我們就可以在模板中通過(guò)判斷 post.toc 是否為空,來(lái)決定是否顯示側(cè)欄目錄:
{% block toc %} {% if post.toc %} <div class="widget widget-content"> <h3 class="widget-title">文章目錄</h3> <div class="toc"> <ul> {{ post.toc|safe }} </ul> </div> </div> {% endif %} {% endblock toc %}
這里我們看到了一個(gè)新的模板標(biāo)簽 {% if %},這個(gè)標(biāo)簽用來(lái)做條件判斷,和 Python 中的 if 條件判斷是類(lèi)似的。
文章內(nèi)容的標(biāo)題被設(shè)置了錨點(diǎn),點(diǎn)擊目錄中的某個(gè)標(biāo)題,頁(yè)面就會(huì)跳到該文章內(nèi)容中標(biāo)題所在的位置,這時(shí)候?yàn)g覽器的 URL 顯示的值可能不太美觀,比如像下面的樣子:
http://127.0.0.1:8000/posts/8/#_1 http://127.0.0.1:8000/posts/8/#_3
#_1 就是錨點(diǎn),Markdown 在設(shè)置錨點(diǎn)時(shí)利用的是標(biāo)題的值,由于通常我們的標(biāo)題都是中文,Markdown 沒(méi)法處理,所以它就忽略的標(biāo)題的值,而是簡(jiǎn)單地在后面加了個(gè) \_1 這樣的錨點(diǎn)值。為了解決這一個(gè)問(wèn)題,需要修改一下傳給 extentions 的參數(shù),其具體做法如下:
blog/views.py from django.utils.text import slugify from markdown.extensions.toc import TocExtension def detail(request, pk): post = get_object_or_404(Post, pk=pk) md = markdown.Markdown(extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', # 記得在頂部引入 TocExtension 和 slugify TocExtension(slugify=slugify), ]) post.body = md.convert(post.body) m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S) post.toc = m.group(1) if m is not None else '' return render(request, 'blog/detail.html', context={'post': post})
和之前不同的是,extensions 中的 toc 拓展不再是字符串 markdown.extensions.toc ,而是 TocExtension 的實(shí)例。TocExtension 在實(shí)例化時(shí)其 slugify 參數(shù)可以接受一個(gè)函數(shù),這個(gè)函數(shù)將被用于處理標(biāo)題的錨點(diǎn)值。Markdown 內(nèi)置的處理方法不能處理中文標(biāo)題,所以我們使用了 django.utils.text 中的 slugify 方法,該方法可以很好地處理中文。
這時(shí)候標(biāo)題的錨點(diǎn) URL 變得好看多了。
http://127.0.0.1:8000/posts/8/#我是標(biāo)題一 http://127.0.0.1:8000/posts/8/#我是標(biāo)題二下的子標(biāo)題
References
[1] HelloGitHub-Team 倉(cāng)庫(kù): https://github.com/HelloGitHub-Team/HelloDjango-blog-tutorial
歡迎關(guān)注 HelloGitHub 公眾號(hào),獲取更多開(kāi)源項(xiàng)目的資料和內(nèi)容
『講解開(kāi)源項(xiàng)目系列』啟動(dòng)——讓對(duì)開(kāi)源項(xiàng)目感興趣的人不再畏懼、讓開(kāi)源項(xiàng)目的發(fā)起者不再孤單。跟著我們的文章,你會(huì)發(fā)現(xiàn)編程的樂(lè)趣、使用和發(fā)現(xiàn)參與開(kāi)源項(xiàng)目如此簡(jiǎn)單。歡迎聯(lián)系我們給我們投稿,讓更多人愛(ài)上開(kāi)源、貢獻(xiàn)開(kāi)源~
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。