制作呢,當(dāng)" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運(yùn)營 > 從破解某定設(shè)計(jì)網(wǎng)站談前端水印(詳細(xì)教程)

從破解某定設(shè)計(jì)網(wǎng)站談前端水印(詳細(xì)教程)

時(shí)間:2023-07-21 09:42:01 | 來源:網(wǎng)站運(yùn)營

時(shí)間:2023-07-21 09:42:01 來源:網(wǎng)站運(yùn)營

從破解某定設(shè)計(jì)網(wǎng)站談前端水印(詳細(xì)教程):

前言

最近在寫公眾號(hào)的時(shí)候,常常會(huì)自己做首圖,并且慢慢地發(fā)現(xiàn)沉迷于制作首圖,感覺扁平化的設(shè)計(jì)的真好好看。慢慢地萌生了一個(gè)做一個(gè)屬于自己的首圖生成器的想法。







制作呢,當(dāng)然也不是拍拍腦袋就開始,在開始之前,就去研究了一下某在線設(shè)計(jì)網(wǎng)站(如果有人不知道的話,可以說一下,這是一個(gè)在線制作海報(bào)之類的網(wǎng)站 T T 像我們這種內(nèi)容創(chuàng)作者用的比較多),畢竟人家已經(jīng)做了很久了,我只是想做個(gè)方便個(gè)人使用的。畢竟以上用 PS 做著還是有一些廢時(shí)間,由于組成的元素都很簡單,做一個(gè)自動(dòng)化生成的完全可以。

但是研究著研究著,就看到了某在線設(shè)計(jì)網(wǎng)站的水印,像這種技術(shù)支持的網(wǎng)站,最重要的防御措施就是水印了,水印能夠很好的保護(hù)知識(shí)產(chǎn)權(quán)。

慢慢地路就走偏了,開始對它的水印感興趣了。不禁發(fā)現(xiàn)之前只是大概知道水印的生成方法,但是從來沒有仔細(xì)研究過,本文將以以下的路線進(jìn)行講解。以下所有代碼示例均在

https://github.com/hua1995116/node-demo/tree/master/watermark



明水印

水印(watermark)是一種容易識(shí)別、被夾于紙內(nèi),能夠透過光線穿過從而顯現(xiàn)出各種不同陰影的技術(shù)。
水印的類型有很多,有一些是整圖覆蓋在圖層上的水印,還有一些是在角落。







那么這個(gè)水印怎么實(shí)現(xiàn)呢?熟悉 PS 的朋友,都知道 PS 有個(gè)叫做圖層的概念。

網(wǎng)頁也是如此。我們可以通過絕對定位,來將水印覆蓋到我們的頁面之上。

最終變成了這個(gè)樣子。

等等,但是發(fā)現(xiàn)少了點(diǎn)什么。直接覆蓋上去,就好像是一個(gè)蒙層,我都知道這樣是無法觸發(fā)底下圖層的事件的,此時(shí)就要介紹一個(gè)css屬性pointer-events。

pointer-events CSS 屬性指定在什么情況下 (如果有) 某個(gè)特定的圖形元素可以成為鼠標(biāo)事件的 target。
當(dāng)它的被設(shè)置為 none 的時(shí)候,能讓元素實(shí)體虛化,雖然存在這個(gè)元素,但是該元素不會(huì)觸發(fā)鼠標(biāo)事件。詳情可以查看 CSS3 pointer-events:none應(yīng)用舉例及擴(kuò)展 ? 張鑫旭-鑫空間-鑫生活 。

這下理清了實(shí)現(xiàn)原理,等于成功了一半了!

明水印的生成

明水印的生成方式主要可以歸為兩類,一種是 純 html 元素(純div),另一種則為背景圖(canvas/svg)。

下面我分別來介紹一下,兩種方式。

div實(shí)現(xiàn)

我們首先來講比較簡單的 div 生成的方式。就按照我們剛才說的。

// 文本內(nèi)容<div class="app"> <h1>秋風(fēng)</h1> <p>hello</p></div>首先我們來生成一個(gè)水印塊,就是上面的 一個(gè)個(gè)秋風(fēng)的筆記。這里主要有一點(diǎn)就是設(shè)置一個(gè)透明度(為了讓水印看起來不是那么明顯,從而不遮擋我們的主要頁面),另一個(gè)就是一個(gè)旋轉(zhuǎn),如果是正的水平會(huì)顯得不是那么好看,最后一點(diǎn)就是使用 userSelect 屬性,讓此時(shí)的文字無法被選中。

userSelect

CSS 屬性 user-select 控制用戶能否選中文本。除了文本框內(nèi),它對被載入為 chrome 的內(nèi)容沒有影響。
function cssHelper(el, prototype) { for (let i in prototype) { el.style[i] = prototype[i] }}const item = document.createElement('div')item.innerHTML = '秋風(fēng)的筆記'cssHelper(item, { position: 'absolute', top: `50px`, left: `50px`, fontSize: `16px`, color: '#000', lineHeight: 1.5, opacity: 0.1, transform: `rotate(-15deg)`, transformOrigin: '0 0', userSelect: 'none', whiteSpace: 'nowrap', overflow: 'hidden',})有了一個(gè)水印片,我們就可以通過計(jì)算屏幕的寬高,以及水印的大小來計(jì)算我們需要生成的水印個(gè)數(shù)。

const waterHeight = 100;const waterWidth = 180;const { clientWidth, clientHeight } = document.documentElement || document.body;const column = Math.ceil(clientWidth / waterWidth);const rows = Math.ceil(clientHeight / waterHeight);for (let i = 0; i < column * rows; i++) { const wrap = document.createElement('div'); cssHelper(wrap, Object.create({ position: 'relative', width: `${waterWidth}px`, height: `${waterHeight}px`, flex: `0 0 ${waterWidth}px`, overflow: 'hidden', })); wrap.appendChild(createItem()); waterWrapper.appendChild(wrap)}document.body.appendChild(waterWrapper)這樣子我們就完美地實(shí)現(xiàn)了上面我們給出的思路的樣子啦。

背景圖實(shí)現(xiàn)

canvas

canvas的實(shí)現(xiàn)很簡單,主要是利用canvas 繪制一個(gè)水印,然后將它轉(zhuǎn)化為 base64 的圖片,通過canvas.toDataURL() 來拿到文件流的 url ,關(guān)于文件流相關(guān)轉(zhuǎn)化可以參考我之前寫的文章一文帶你層層解鎖「文件下載」的奧秘, 然后將獲取的 url 填充在一個(gè)元素的背景中,然后我們設(shè)置背景圖片的屬性為重復(fù)。

.watermark { position: fixed; top: 0px; right: 0px; bottom: 0px; left: 0px; pointer-events: none; background-repeat: repeat;}function createWaterMark() { const angle = -20; const txt = '秋風(fēng)的筆記' const canvas = document.createElement('canvas'); canvas.width = 180; canvas.height = 100; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, 180, 100); ctx.fillStyle = '#000'; ctx.globalAlpha = 0.1; ctx.font = `16px serif` ctx.rotate(Math.PI / 180 * angle); ctx.fillText(txt, 0, 50); return canvas.toDataURL();}const watermakr = document.createElement('div');watermakr.className = 'watermark';watermakr.style.backgroundImage = `url(${createWaterMark()})`document.body.appendChild(watermakr);

svg

svg 和 canvas 類似,主要還是生成背景圖片。

function createWaterMark() { const svgStr = `<svg xmlns="http://www.w3.org/2000/svg" width="180px" height="100px"> <text x="0px" y="30px" dy="16px" text-anchor="start" stroke="#000" stroke-opacity="0.1" fill="none" transform="rotate(-20)" font-weight="100" font-size="16" > 秋風(fēng)的筆記 </text> </svg>`; return `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svgStr)))}`;}const watermakr = document.createElement('div');watermakr.className = 'watermark';watermakr.style.backgroundImage = `url(${createWaterMark()})`document.body.appendChild(watermakr);

明水印的破解一

以上就很快實(shí)現(xiàn)了水印的幾種方案。但是對于有心之人來說,肯定會(huì)想著破解,以上破解也很簡單。

打開了Chrome Devtools 找到對應(yīng)的元素,直接按 delete 即可刪除。

明水印的防御

這樣子的水印對于大概知道控制臺(tái)操作的小白就可以輕松破解,那么有什么辦法能防御住這樣的操作呢?

答案是肯定的,js 有一個(gè)方法叫做 MutationObserver,能夠監(jiān)控元素的改動(dòng)。

MutationObserver 對現(xiàn)代瀏覽的兼容性還是不錯(cuò)的,MutationObserver是元素觀察器,字面上就可以理解這是用來觀察Node(節(jié)點(diǎn))變化的。MutationObserver是在DOM4規(guī)范中定義的,它的前身是MutationEvent事件,最低支持版本為 ie9 ,目前已經(jīng)被棄用。

在這里我們主要觀察的有三點(diǎn)

來通過 MDN 查看該方法的使用示例。

const targetNode = document.getElementById('some-id');// 觀察器的配置(需要觀察什么變動(dòng))const config = { attributes: true, childList: true, subtree: true };// 當(dāng)觀察到變動(dòng)時(shí)執(zhí)行的回調(diào)函數(shù)const callback = function(mutationsList, observer) { // Use traditional 'for loops' for IE 11 for(let mutation of mutationsList) { if (mutation.type === 'childList') { console.log('A child node has been added or removed.'); } else if (mutation.type === 'attributes') { console.log('The ' + mutation.attributeName + ' attribute was modified.'); } }};// 創(chuàng)建一個(gè)觀察器實(shí)例并傳入回調(diào)函數(shù)const observer = new MutationObserver(callback);// 以上述配置開始觀察目標(biāo)節(jié)點(diǎn)observer.observe(targetNode, config);MutationObserver主要是監(jiān)聽子元素的改動(dòng),因此我們的監(jiān)聽對象為 document.body, 一旦監(jiān)聽到我們的水印元素被刪除,或者屬性修改,我們就重新生成一個(gè)。通過以上示例,加上我們的思路,很快我們就寫一個(gè)監(jiān)聽刪除元素的示例。(監(jiān)聽屬性修改也是類似就不一一展示了)

// 觀察器的配置(需要觀察什么變動(dòng))const config = { attributes: true, childList: true, subtree: true };// 當(dāng)觀察到變動(dòng)時(shí)執(zhí)行的回調(diào)函數(shù)const callback = function (mutationsList, observer) {// Use traditional 'for loops' for IE 11 for (let mutation of mutationsList) { mutation.removedNodes.forEach(function (item) { if (item === watermakr) { document.body.appendChild(watermakr); } }); }};// 監(jiān)聽元素const targetNode = document.body;// 創(chuàng)建一個(gè)觀察器實(shí)例并傳入回調(diào)函數(shù)const observer = new MutationObserver(callback);// 以上述配置開始觀察目標(biāo)節(jié)點(diǎn)observer.observe(targetNode, config);我們打開控制臺(tái)來檢驗(yàn)一下。

這回完美了,能夠完美抵御一些開發(fā)小白了。

那么這樣就萬無一失了嗎?顯然,道高一尺魔高一丈,畢竟前端的一切都是不安全的。

明水印的破解二

在一個(gè)高級(jí)前端工程師面前,一切都是紙老虎。接下來我就隨便介紹三種破解的方式。

第一種

打開Chrome Devtools,點(diǎn)擊設(shè)置 - Debugger - Disabled JavaScript .

然后再打開頁面,delete我們的水印元素。

第二種

復(fù)制一個(gè) body 元素,然后將原來 body 元素的刪除。

第三種

打開一個(gè)代理工具,例如 charles,將生成水印相關(guān)的代碼刪除。

破解實(shí)踐

接下來我們實(shí)戰(zhàn)一下,通過預(yù)先分析,我們看到某在線設(shè)計(jì)網(wǎng)站的內(nèi)容是以 div 的方式實(shí)現(xiàn)的,所以可以利用這種方案。 打開 https://www.gaoding.com/design?id=33931419&simple=1 (僅供舉例學(xué)習(xí))

打開控制臺(tái),Ctrl + F 搜索 watermark 相關(guān)字眼。(這一步是作為一個(gè)程序員的直覺,基本上你要找什么,搜索對應(yīng)的英文就可以 ~)

很快我們就找到了水印圖。發(fā)現(xiàn)直接刪除,沒有辦法刪除水印元素,根據(jù)我們剛才學(xué)習(xí)的,肯定是利用了MutationObserver 方法。我們使用我們的第一個(gè)破解方法,將 JavaScript 禁用,再將元素刪除。

水印已經(jīng)消失了。

但是這樣真的就萬事大吉了嗎?

不知道你有沒有聽過一種東西,看不見摸不著,但是它卻真實(shí)存在,他的名字叫做暗水印,我們將時(shí)間倒流到 16 年間的月餅門事件,因?yàn)橛袉T工將內(nèi)網(wǎng)站點(diǎn)截圖了,但是很快被定位出是誰截圖了。

雖然你將一些可見的水印去除了,但是還會(huì)存在一些不可見的保護(hù)版權(quán)的水印。(這就是防止一些壞人拿去作另外的用途)

暗水印

暗水印是一種肉眼不可見的水印方式,可以保持圖片美觀的同時(shí),保護(hù)你的資源版權(quán)。
暗水印的生成方式有很多,常見的為通過修改RGB 分量值的小量變動(dòng)、DWT、DCT 和 FFT 等等方法。

通過介紹前端實(shí)現(xiàn) RGB 分量值的小量變動(dòng) 來揭秘其中的奧秘,主要參考 不能說的秘密——前端也能玩的圖片隱寫術(shù) | AlloyTeam。

我們都知道圖片都是有一個(gè)個(gè)像素點(diǎn)構(gòu)成的,每個(gè)像素點(diǎn)都是由 RGB 三種元素構(gòu)成。當(dāng)我們把其中的一個(gè)分量修改,人的肉眼是很難看出其中的變化,甚至是像素眼的設(shè)計(jì)師也很難分辨出。

你能看出其中的差別嗎?根據(jù)這個(gè)原理,我們就來實(shí)踐吧。(女孩子可以掌握方法后可以拿以下圖片進(jìn)行試驗(yàn)測試)

首先拿到以上圖片,我們先來講解解碼方式,解碼其實(shí)很簡單,我們需要?jiǎng)?chuàng)建一個(gè)規(guī)律,再通過我們的規(guī)律去解碼?,F(xiàn)在假設(shè)的規(guī)律為,我們將所有像素的 R 通道的值為奇數(shù)的時(shí)候我們創(chuàng)建的通道密碼,舉個(gè)簡單的例子。

例如我們把以上當(dāng)做是一個(gè)圖形,加入他要和一個(gè)中文的 "一" 放進(jìn)圖像,例如我們將 "一" 放入第二行。按照我們的算法,我們的圖像會(huì)變成這個(gè)樣子。

解碼的時(shí)候,我們拿到所有的奇數(shù)像素將它渲染出來,例如這里的 '5779' 是不是正好是一個(gè) "一",下面就轉(zhuǎn)化為實(shí)踐。

解碼過程

首先創(chuàng)建一個(gè) canvas 標(biāo)簽。

<canvas id="canvas" width="256" height="256"></canvas>var ctx = document.getElementById('canvas').getContext('2d');var img = new Image();var originalData;img.onload = function () { // canvas像素信息 ctx.drawImage(img, 0, 0); originalData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); console.log() processData(ctx, originalData)};img.src = 'qiufeng-super.png';我們打印出這個(gè)數(shù)組,會(huì)有一個(gè)非常大的數(shù)組,一共有 256 * 256 * 4 = 262144 個(gè)值。因?yàn)槊總€(gè)像素除了 RGB 外還有一個(gè) alpha 通道,也就是我們常用的透明度。

上面也說了,我們的 R 通道為奇數(shù)的時(shí)候 ,就我們的解密密碼。因此我們只需要所有的像素點(diǎn)的 R 通道為奇數(shù)的時(shí)候,將它填填充,不為奇數(shù)的時(shí)候就不填充,很快我們就能得到我們的隱藏圖像。

var processData = function (ctx, originalData) { var data = originalData.data; for (var i = 0; i < data.length; i++) { if (i % 4 == 0) { // R分量 if (data[i] % 2 == 0) { data[i] = 0; } else { data[i] = 255; } } else if (i % 4 == 3) { // alpha通道不做處理 continue; } else { // 關(guān)閉其他分量,不關(guān)閉也不影響答案 data[i] = 0; } } // 將結(jié)果繪制到畫布 ctx.putImageData(originalData, 0, 0);}processData(ctx, originalData)解密完會(huì)出現(xiàn)類似于以下這個(gè)樣子。

那我們?nèi)绾渭用艿模蔷拖喾吹姆绞骄涂梢岳病?這里都用了 不能說的秘密——前端也能玩的圖片隱寫術(shù) 中的例子,= = 我也能寫出一個(gè)例子,但是覺得沒必要,別人已經(jīng)寫得很好了,我們只是講述這個(gè)方法,需要代碼來舉例而已)

編碼過程

加密呢,首先我們需要獲取加密的圖像信息。

var textData;var ctx = document.getElementById('canvas').getContext('2d');ctx.font = '30px Microsoft Yahei';ctx.fillText('秋風(fēng)的筆記', 60, 130);textData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data;然后提取加密信息在待加密的圖片上進(jìn)行處理。

var mergeData = function (ctx, newData, color, originalData) { var oData = originalData.data; var bit, offset; // offset的作用是找到alpha通道值,這里需要大家自己動(dòng)動(dòng)腦筋 switch (color) { case 'R': bit = 0; offset = 3; break; case 'G': bit = 1; offset = 2; break; case 'B': bit = 2; offset = 1; break; } for (var i = 0; i < oData.length; i++) { if (i % 4 == bit) { // 只處理目標(biāo)通道 if (newData[i + offset] === 0 && (oData[i] % 2 === 1)) { // 沒有信息的像素,該通道最低位置0,但不要越界 if (oData[i] === 255) { oData[i]--; } else { oData[i]++; } } else if (newData[i + offset] !== 0 && (oData[i] % 2 === 0)) { // // 有信息的像素,該通道最低位置1,可以想想上面的斑點(diǎn)效果是怎么實(shí)現(xiàn)的 oData[i]++; } } } ctx.putImageData(originalData, 0, 0);}主要的思路還是我一開始所講的,在有像素信息的點(diǎn),將 R 偶數(shù)的通道+1。在沒有像素點(diǎn)的地方將 R 通道轉(zhuǎn)化成偶數(shù),最后在 img.onload 調(diào)用 processData(ctx, originalData) 。

img.onload = function () { // 獲取指定區(qū)域的canvas像素信息 ctx.drawImage(img, 0, 0); originalData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); console.log(originalData) processData(ctx, originalData)};以上方法就是一種比較簡單的加密方式。以上代碼都放到了倉庫 watermark/demo/canvas-dark-watermark.html 路徑下,方法都封裝好了~。

但是實(shí)際過程需要更專業(yè)的加密方式,例如利用傅里葉變化公式,來進(jìn)行頻域制定數(shù)字盲水印,這里就不詳細(xì)展開講了,以后研究完再詳細(xì)講~




破解實(shí)踐

聽完上述的介紹,那么某在線設(shè)計(jì)網(wǎng)站是不是很有可能使用了暗水印呢?

當(dāng)然啦,通過我對某在線設(shè)計(jì)網(wǎng)站的分析,我分析了以下幾種情況,我們一一來進(jìn)行測試。

我們先通過免費(fèi)下載的圖片來進(jìn)行分析。打開 https://www.gaoding.com/design?id=13964513159025728&mode=user

通過實(shí)驗(yàn)(實(shí)驗(yàn)主要是去分析他各個(gè)場景下觸發(fā)的請求),發(fā)現(xiàn)在下載免費(fèi)圖片的時(shí)候,發(fā)現(xiàn)它都會(huì)去向阿里云發(fā)送一個(gè) POST 請求,這熟悉的請求域名以及熟悉的數(shù)據(jù)封裝方式,這不就是 阿里云 OSS 客戶端上傳方式嘛。這就好辦了,我們?nèi)ゲ樵円幌掳⒗镌剖欠裼猩砂邓〉南嚓P(guān)方式,從而來看看某在線設(shè)計(jì)網(wǎng)站是否含有暗水印。很快我們就從官方文檔搜索到了相關(guān)的文檔,且對于低 QPS 是免費(fèi)的。(這就是最好理解的連帶效應(yīng),例如我們覺得耐克阿迪啥賣運(yùn)動(dòng)類服飾,你買了他的鞋子,可能還會(huì)想買他的衣服)

const { RPCClient } = require("@alicloud/pop-core");var client = new RPCClient({ endpoint: "http://imm.cn-shenzhen.aliyuncs.com", accessKeyId: 'xxx', accessKeySecret: 'xxx', apiVersion: "2017-09-06",});(async () => { try { var params = { Project: "test-project", ImageUri: "oss://watermark-shenzheng/source/20201009-182331-fd5a.png", TargetUri: "oss://watermark-shenzheng/dist/20201009-182331-fd5a-out.jpg", Model: "DWT" }; var result = await client.request("DecodeBlindWatermark", params); console.log(result); } catch (err) { console.log(err); }})()我們寫了一個(gè)demo進(jìn)行了測試。由于阿里云含有多種暗水印加密方式,為啥我使用了 DWT 呢?因?yàn)槠渌麕追N都需要原圖,而我們剛才的測試,他上傳只會(huì)上傳一個(gè)文件到 OSS ,因此大致上排除了需要原圖的方案。

但是我們的結(jié)果卻沒有發(fā)現(xiàn)任何加密的跡象。

為什么我們會(huì)去猜想阿里云的圖片暗水印的方式?因?yàn)閺纳蟼鞯慕嵌葋砜紤],我們上傳的圖片 key 的地址即是我們下載的圖片,也就是現(xiàn)在存在兩種情況,一就是通過阿里云的盲水印方案,另一種就是上傳前進(jìn)行了水印的植入?,F(xiàn)在看來不是阿里云水印的方案,那么只是能是上傳前就有了水印。

這個(gè)過程就有兩種情況,一是生成的過程中加入的水印,前端加入的水印。二是物料圖含有水印。

對于第一種情況,我們可以通過 dom-to-image 這個(gè)庫,在前端直接進(jìn)行下載,或者使用截圖的方式。目前通過直接下載和通過站點(diǎn)內(nèi)生成,發(fā)現(xiàn)元素略有不同。

第一個(gè)為我通過 dom-to-image 的方式下載,第二種為站點(diǎn)內(nèi)下載,明顯大了一些。(有點(diǎn)懷疑他在圖片生成中可能做了什么手腳)

但是感覺前端加密的方式比較容易破解,最壞的情況想到了對素材進(jìn)行了加密,但是這樣的話就無從破解了(但是查閱了一些資料,由于某在線設(shè)計(jì)網(wǎng)站站點(diǎn)素材大多是透明背景的,這種加密效果可能會(huì)弱一些,以后牛逼了再來補(bǔ)充)。目前這一塊暫時(shí)還不清楚,探究止步于此了。

攻擊實(shí)驗(yàn)

那如果一張圖經(jīng)過暗水印加密,他的抵抗攻擊性又是如何呢?

這是一張通過阿里云 DWT暗水印進(jìn)行的加密,解密后的樣子為"秋風(fēng)"字樣,我們分別來測試一下。

加一些元素

結(jié)果: 識(shí)別效果不錯(cuò)

截圖

結(jié)果: 識(shí)別效果不錯(cuò)

大小變化

結(jié)果:識(shí)別效果不錯(cuò)

加蒙層

結(jié)果: 直接就拉胯了。

可見,暗水印的抵抗攻擊性還是蠻強(qiáng)的,是一種比較好的抵御攻擊的方式~

最后

以上僅僅為技術(shù)交流~ 大家不要在實(shí)際的場景盲目使用,商業(yè)項(xiàng)目違規(guī)使用后果自負(fù) ~ 或者期待一下我接下來想搞的這個(gè)個(gè)人免費(fèi)首圖生成器~ 喜歡文章的小伙伴可以點(diǎn)個(gè)贊哦 ~ 歡迎關(guān)注公眾號(hào) 秋風(fēng)的筆記 ,學(xué)習(xí)前端不迷路。

參考

https://imm.console.aliyun.com/cn-shenzhen/project?accounttraceid=1280c6af416744a38e9acf63c4e0878cjdet

https://help.aliyun.com/document_detail/138800.html?spm=a2c4g.11186623.6.656.3bd46bb4oglhEr

https://oss.console.aliyun.com/bucket/oss-cn-shenzhen/watermark-shenzheng/object?path=dist%2F

https://juejin.cn/post/6844903650054111246

https://www.zhihu.com/question/50677827/answer/122388524

https://www.zhihu.com/question/50735753

關(guān)鍵詞:水印,教程,詳細(xì),設(shè)計(jì)

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉