實戰(zhàn))
簡介eWebEditor v8.0 是一套基于瀏覽器的所見即所得在線網(wǎng)頁編輯器完整程序包面向網(wǎng)站開發(fā)者、內容管理系統(tǒng)集成人員以及需要在線內容編輯功能的技術運維者。資源共606個文件壓縮包約4.29MB包含ASP動態(tài)腳本、JavaScript交互邏輯、CSS樣式表、HTML示例頁面以及大量GIF/JPG圖標素材部署后可直接運行并快速接入現(xiàn)有網(wǎng)站后臺。已有194人學習下載。包內還附帶了配置示例、上傳處理及樣式定制等核心文件便于讀者理清編輯器初始化、文件上傳和外觀調整等關鍵流程并在此基礎上按需修改功能或進行二次開發(fā)。該編輯器支持多平臺和主流瀏覽器提供字體、圖片、鏈接、表格等豐富編輯工具能有效降低內容發(fā)布門檻適合需要在自有系統(tǒng)中集成成熟在線編輯組件的中級開發(fā)者使用。1. eWebEditor v8.0 是什么textarea 時代的所見即所得今天還在跑eWebEditor v8.0 是 2010 年前后國內 CMS、OA、教務系統(tǒng)里最常見的所見即所得在線 HTML 編輯器。在 textarea 還統(tǒng)治著后臺錄入的年代它用一組 JavaScript 腳本加 iframe 模擬出帶工具欄、上傳和樣式管理的編輯界面讓編輯不用手敲 HTML 就能排出帶圖文章。到今天很多老系統(tǒng)的后臺還在用它搜索引擎里對這個版本的提問也一直沒斷過。如果你接手的是帶它的項目先別急著換編輯器——先看懂它的部署方式和那幾個高頻故障點很多時候改幾句話就能讓一個老功能繼續(xù)穩(wěn)定服役這比推倒重來的代價小得多。2. 部署 eWebEditor v8.0從解壓目錄到替換 textarea 的最小接入要接入一個老編輯器先把它的文件結構摸清楚。v8.0 的壓縮包解開后通常是一個 ewebeditor 目錄里面按功能分好了 js、css、images、語言包和處理上傳的服務端腳本。下面這張表是我見過最多的目錄構成不同版本的目錄名可能有出入但職能是固定的。2.1 解壓后目錄里通常躺著哪些文件目錄 / 文件作用ewebeditor.js編輯器主程序創(chuàng)建實例和暴露 API 的入口ewebeditor_main.js / dialog 相關對話框、彈出層邏輯通常被主程序按需加載css / styles工具欄外觀、編輯區(qū)默認樣式的樣式表images按鈕圖標、皮膚背景圖按鈕 id 與這里的文件名一一對應language語言包常見是簡體中文和英文兩份編碼upload.asp / upload.aspx / upload.php / upload.jsp接收編輯器上傳文件的服務端腳本按網(wǎng)站技術棧選用其中一個inc / include配置類文件存放上傳路徑、類型限制等運行時參數(shù)看到 upload 那一行就要有警覺編輯器本身的 js 只負責把文件 POST 給這個腳本文件存哪里、能不能存、叫什么名字全由腳本決定前端配置只在界面上做提示。第 3 章和第 5 章還會反復回到這個點。接入流程是把整個 ewebeditor 目錄原樣拷進項目的靜態(tài)資源根目錄比如站點根目錄下的 /ewebeditor/。不要只挑幾個 js 文件拷走編輯器運行時會按相對路徑去取皮膚、圖標和語言包缺一個就白屏。還要注意目錄里的文件名和大小寫盡量別動v8.0 對路徑和文件名都比較敏感改名很容易讓皮膚加載失效。2.2 用腳本創(chuàng)建編輯器new eWebEditor 與 create()頁面里原本用來錄入內容的是一個 textareaeWebEditor 的思路是讓它保留在表單里再用編輯器界面覆蓋它。最小接入代碼是這樣script typetext/javascript src/ewebeditor/ewebeditor.js/script !-- 編輯器的內容最終要寫回這個 textarea據(jù)此提交給服務端 -- form idarticleForm action/admin/save.php methodpost textarea idcontent namecontent stylewidth:800px;height:400px;/textarea button typebutton onclickbeforeSubmit()保存文章/button /form script typetext/javascript // 第一個參數(shù)是 textarea 的 id第二個參數(shù)是工具條模式simple / standard / full var editor new eWebEditor(content, full); // BasePath 必須指向編輯器目錄寫錯會出現(xiàn)按鈕圖標丟失、編輯區(qū)空白 editor.BasePath /ewebeditor/; // 可以用像素值也可以用百分比 editor.width 100%; editor.height 480px; // create() 執(zhí)行時textarea 會被編輯器整體替換成界面 editor.create(); /script這段代碼里最容易被忽略的是 BasePath 的結尾斜杠。v8.0 把腳本、皮膚、語言包的加載都拼接在 BasePath 后面如果少了斜杠請求會變成 /ewebeditorcss/...瀏覽器直接 404后果就是編輯器區(qū)域空白或者只剩一排沒有圖標的文字按鈕。我見過的項目里有人寫死絕對路徑有人用相對路徑有人用 js 動態(tài)探測穩(wěn)定交接的經(jīng)驗是寫絕對路徑從站點根目錄起步。創(chuàng)建完成后textarea 會被隱藏。這里有一個最容易造成“文章保存后是空的”的坑編輯器是編輯器textarea 是 textarea兩者之間不會自動同步必須手動回寫。另外如果頁面需要兩個編輯器同時存在只要用不同的 textarea id 各 new 一個實例就行兩者互不干擾但記得給每個實例單獨配 BasePath 和高度。2.3 提交前把編輯內容回寫進 textarea保存動作發(fā)生時服務端讀的是 textarea 的 value而不是 editor 對象內部的內容。所以表單提交必須經(jīng)過一層回寫// 在文章表單的提交函數(shù)里先把編輯器里的 HTML 取出來放回 textarea function beforeSubmit() { // 常見版本用 contentHtml() 取編輯區(qū)內容個別版本叫 getHtml()看實際包里的方法名 var html editor.contentHtml(); // 放回 textareanamecontent 的字段才會隨表單提交 document.getElementById(content).value html; // 如果編輯器內容為空可以在這里做校驗 if (html.replace(/[^]/g, ).trim() ) { alert(正文不能為空); return false; } return true; }這里的邏輯是顯式地把編輯器內容寫到表單字段再做一次去標簽空文本的輕量校驗。很多接入失敗的案例都繞過了這一步頁面上編輯有內容提交后數(shù)據(jù)庫里是空串排查半天發(fā)現(xiàn) textarea 從來沒被賦值。把回寫放在 submit 之前的按鈕事件里是最常見也最不容易漏的做法。如果表單是用 ajax 序列化提交的同樣要在序列化之前手動執(zhí)行這段回寫把 content 字段更新成編輯器當前內容。有些項目偷懶只在頁面加載時賦值一次用戶改完內容直接提交庫里永遠是最初那一版這類問題在論壇里被當成靈異事件問過很多次其實就是沒有回寫。部署這章做到這里一個能錄入、能保存的最小閉環(huán)就成立了。下一步是把它按業(yè)務調成想要的樣子。3. 配置 eWebEditor v8.0工具欄、上傳與內容區(qū)樣式三組必調參數(shù)編輯器接入只是第一步真正對接業(yè)務需求的是配置層。v8.0 的配置散在三處創(chuàng)建實例時傳的模式、實例上的屬性賦值、以及服務端上傳腳本里的參數(shù)。這一章按使用頻率排三個方向工具條、上傳、內容區(qū)樣式。調好這三組大部分后臺需求就能覆蓋。3.1 工具條模式與自定義按鈕順序模式參數(shù) full / standard / simple 只是三套預設實際項目里經(jīng)常要按角色給權限。比如普通編輯不讓他看到插入代碼、直接改源碼的按鈕管理員才看到完整工具條。常見做法是給不同角色各創(chuàng)建一份配置。// 普通編輯走簡潔模式隱藏源碼和上傳相關按鈕 var editor new eWebEditor(content, standard); editor.toolBar Cut,Copy,Paste,|,Undo,Redo,|,FontName,FontSize,|,Bold,Italic,Underline,|,ForeColor,BackColor,|,UnorderedList,OrderedList,|,Link,Unlink; // 管理員在標準基礎上追加插入表格和代碼 var adminEditor new eWebEditor(content, full); adminEditor.toolBar editor.toolBar ,|,Table,|,Image,Flash,|,Source;工具條的每個按鈕 id 要和 images 目錄里的圖標文件名對應少一個圖標最多是顯示空白不影響功能。但按鈕 id 拼寫錯了會直接不出現(xiàn)。想確認有哪些按鈕可用去 images 目錄看圖標文件名或者看語言包里列出的按鈕標題比猜命名要可靠。豎線是分組分隔符換行由編輯器根據(jù)寬度自適應不要試圖用空格去對齊。配置里一個常見玄學是給 toolBar 賦值之后再調用 create()create 之后再去改 toolBar 往往不生效。所以要養(yǎng)成“先配置屬性、最后 create”的順序習慣避免為了加一個按鈕去刷新頁面。角色類的工具條差異建議放到后端下發(fā)登錄時把該角色的工具條字符串渲染進頁面而不是在前端寫死判斷。3.2 上傳參數(shù)前端限制只是提示真正的閘門在服務端編輯器的上傳按鈕是一個獨立表單把文件發(fā)到 upload.asp 之類的腳本。前端這邊能配的通常只有上傳地址和回顯路徑格式類型與大小限制主要寫在服務端腳本里。以當年最常見的 PHP 版為例?php // upload.php 的關鍵參數(shù)允許擴展名、保存目錄、生成文件名 $allowExt array(gif, jpg, jpeg, png, doc, docx, xls, xlsx, pdf, zip); // 保存到按日期切分的目錄避免單目錄文件過多 $saveDir ../uploads/ . date(Ymd); if (!file_exists($saveDir)) { mkdir($saveDir, 0755, true); } // 文件名重命名時間戳加隨機數(shù)避免中文名和重名文件 $ext strtolower(pathinfo($_FILES[upload][name], PATHINFO_EXTENSION)); if (!in_array($ext, $allowExt)) { exit(文件類型不允許); } $newName date(YmdHis) . _ . rand(1000, 9999) . . . $ext; move_uploaded_file($_FILES[upload][tmp_name], $saveDir . / . $newName); echo $saveDir . / . $newName; // 編輯器用返回值拼圖片 URL這段腳本透露兩個重點。一是保存目錄必須存在且有寫入權限很多上傳失敗的報錯都來自目錄不可寫日志里表現(xiàn)為 move_uploaded_file 返回 false。二是返回給編輯器的 URL 決定了回顯地址如果返回的是相對路徑而編輯器頁面在別的目錄層級圖片就會打不開。穩(wěn)妥做法是返回完整 URL或者在入口處用常量拼出站點地址。常見的上傳參數(shù)配置項如下不同版本叫法略有差異參數(shù)作用建議值允許擴展名白名單列表只留業(yè)務需要的格式圖片類建議 jpg/jpeg/png/gif保存目錄文件落盤位置獨立于站點根的 uploads 目錄按日期分子目錄文件重命名避免原文件名上盤日期 隨機數(shù)不保留用戶原始文件名回顯 URL編輯器拼圖片地址完整 URL 或站內根路徑不返回相對路徑還有一類上傳失敗的現(xiàn)場特別像網(wǎng)絡問題點按鈕后進度條走完編輯器里沒有圖。去服務端看返回的字段大多數(shù)情況是類型不在白名單里或者體積超過腳本里未設置的上限。不要在前端死等直接開瀏覽器開發(fā)者工具看 upload 腳本的 HTTP 響應msg 或返回值里會寫明拒絕原因。3.3 內容區(qū)樣式讓編輯效果貼近前臺正文后臺編輯效果和前臺展示不一致是富文本編輯器被吐槽最多的地方。根因是編輯區(qū) iframe 用的是編輯器自帶樣式和前臺頁面的 font、line-height、圖片邊距不一樣。v8.0 支持通過參數(shù)覆蓋編輯區(qū)默認樣式。// 把前臺正文頁的 CSS 核心規(guī)則導入編輯區(qū) editor.styleBody body{font:14px/1.8 Helvetica Neue,PingFang SC,Microsoft YaHei; color:#333;margin:12px;} img{max-width:100%;height:auto;} p{margin:0 0 12px;} table{border-collapse:collapse;width:auto;} td,th{border:1px solid #ddd;padding:6px 10px;}; editor.create();這里寫的是一條完整的 CSS 字符串create 時編輯器會把它注入到編輯區(qū) iframe 的 head 里。對應的前臺頁面只需要保證同名規(guī)則一致編輯時看到的就是接近最終效果的排版。注意不要用外鏈 / 方式去引前臺樣式因為編輯區(qū) iframe 的基準路徑和后臺頁面不同外鏈很容易失效而且前臺全量 CSS 里帶響應式規(guī)則時編輯區(qū)的窄寬度會被布局規(guī)則干擾出現(xiàn)邊距錯亂。還有一個容易翻車的細節(jié)前臺樣式中如果寫了 * { box-sizing: border-box; } 之類的通配符它會繼承到編輯區(qū)嗎v8.0 的編輯區(qū)是獨立 iframe 文檔前臺頁面的全局選擇器進不去只有通過 styleBody 顯式注入的規(guī)則才生效。這是它的隔離邊界也是它相對新版編輯器的樸實之處——沒有多處封裝一條字符串搞定反而好排查。配置調完接下來進入最常見的運行期問題排查。4. 避坑指南v8.0 常見的兼容、亂碼與上傳問題排查跑通部署和配置只是開始v8.0 在老后臺里能穩(wěn)定跑起來繞不開下面幾類高頻故障。這一章按現(xiàn)象、原因、解決的順序來寫都是我實際遇到過且能穩(wěn)定復現(xiàn)的現(xiàn)場。4.1 頁面打開編輯器空白或只有一排按鈕文字現(xiàn)象整個編輯區(qū)渲染不出來只有 toolbar 區(qū)域孤零零幾個文字按鈕或者 iframe 白屏。原因八成是 BasePath 配錯或者編輯器目錄里的 js、css、語言包沒有按相對路徑加載到。v8.0 對 BasePath 非常敏感多一個少一個斜杠都會讓后續(xù)請求 404另外頁面本身如果用了靜態(tài)資源合并插件攔截了 ewebeditor.js 也可能白屏。解決瀏覽器開發(fā)者工具切到 Network過濾編輯器目錄路徑看哪些請求返回 404逐個修正后刷新。要特別檢查入口頁是否部署在子目錄比如后臺地址是 /admin/而編輯器在 /ewebeditor/BasePath 要寫成來自根目錄的絕對路徑而不是相對路徑 ../ewebeditor/后者在當前頁面 URL 帶參數(shù)時極易解析錯。4.2 上傳成功但圖片不顯示或圖片 URL 是相對路徑導致前臺 404現(xiàn)象編輯器里插入圖片后后臺頁面能看到前臺文章頁圖片裂開或者編輯器里也立刻裂開。原因上傳腳本返回的是相對路徑比如 ../../uploads/xxx.jpg從編輯器所在 iframe 的 URL 出發(fā)解析時路徑層級與預期不符。v8.0 的圖片回顯是直接拼在里的返回值是什么瀏覽器就按什么解析。解決改造服務端腳本返回絕對 URL用站點配置的域名開頭拼比如 $domain . $saveDir . / . $newName。如果系統(tǒng)部署在多個域名或 CDN 后面至少也要保證返回 /uploads/... 這種以根斜杠開頭的站內絕對路徑不與當前頁面層級綁定。改完上傳腳本后記得清一下編輯器緩存有些瀏覽器對 iframe 內容緩存得厲害。4.3 保存到數(shù)據(jù)庫后再讀取中文變成問號或亂碼現(xiàn)象編輯器里顯示正常提交后庫里保存的是 ????? 或者類似字符替換的亂碼。原因頁面是 UTF-8但語言包、上傳腳本或數(shù)據(jù)庫連接是 GBK字符在傳輸和存儲之間編碼不一致。編輯器本身不轉碼它把 HTML 原文交給 textarea后續(xù)全交給服務端。解決先把語言包文件轉成 UTF-8保證編輯器輸出字符不先壞掉再檢查服務端腳本和數(shù)據(jù)庫連接的字符集以 MySQL 為例連接后執(zhí)行 set names utf8mb4。這三處統(tǒng)一成同一種編碼后亂碼會消失。排查順序是先看瀏覽器 Network 里表單提交的原始字節(jié)再看服務端接受到的內容最后看庫里字段哪一步開始壞問題就在哪一段。我曾經(jīng)排查過一個案例頁面和語言包都是 UTF-8結果 upload.php 是用 GBK 寫的 include 文件編輯器內容經(jīng)它轉發(fā)時整體亂掉這種藏在中間層的編碼問題最費時間。4.4 編輯區(qū)內文字和字號不對或被頁面全局樣式帶偏現(xiàn)象在后臺編輯時正文突然變大或變小段間距時有時無某些操作后編輯區(qū)出現(xiàn)頁面 header 的樣式。原因常見是 styleBody 沒有按預期注入或者配置里寫入了帶 html, body 選擇器的規(guī)則把編輯區(qū)外層的后臺頁面元素也波及了。另一個來源是編輯器默認樣式被局部修改后又升級覆蓋導致版本間差異。解決回退到最小可復現(xiàn)配置清空 styleBody讓編輯器回到自帶默認樣式確認是否恢復。如果恢復了再逐條加業(yè)務樣式每次加完刷新驗證。經(jīng)驗是 styleBody 里只寫正文相關的標簽規(guī)則不要寫通用通配符也不要用 !important否則未來想覆蓋會非常痛苦。4.5 上傳接口成為 getshell 入口老版本最臭名昭著的漏洞現(xiàn)象服務器被上傳了一個 .asp 或 .php 文件網(wǎng)站隨即被拿下。這類事件在 eWebEditor 系的老系統(tǒng)上屢見不鮮歷史上有過批量被掃描的記錄很多安全通告里都點過它的名。原因上傳腳本只按擴展名黑名單過濾或者根本沒過濾文件名沒有重命名直接保留用戶上傳時的名字保存目錄還位于站點根且可執(zhí)行腳本。三者疊加就是一條直達權限的通道這也是很多安全老鳥對 v8.0 印象深刻的根由。解決上傳腳本必須做三件事——擴展名白名單、文件中真實類型判斷、保存文件名重命名為隨機名。保存目錄要獨立并禁止執(zhí)行腳本如 IIS 下目錄不分配腳本權限、Nginx 或 Apache 下配置該目錄不可解析 PHP。更保守的做法是把上傳文件收進服務端主動生成的日期目錄并在入口做驗證。這部分在第 5 章會給出可直接抄的改造代碼。5. 二次開發(fā) v8.0自定義按鈕與上傳服務端安全改造老編輯器換不掉的時候需求還會繼續(xù)往里加。v8.0 的擴展點主要在兩個地方工具條按鈕的注冊與回調、上傳腳本的改造。把這兩個點吃透多數(shù)定制需求都能接住。第 4 章最后提到的安全改造也一并在這章落地。5.1 往工具條上加一個自定義按鈕自定義按鈕的套路是先在工具條字符串里聲明按鈕 id再在回調里攔截這個 id 做動作。按鈕需要一張圖標尺寸通常與 v8.0 內置圖標一致大約 20×20 像素左右格式用 gif 或 png 都行。// 在標準工具條末尾追加一個自定義按鈕 CustomTip var editor new eWebEditor(content, full); editor.toolBar Cut,Copy,Paste,|,Undo,Redo,|,Bold,Italic,Underline,|,CustomTip; editor.create(); // 按鈕點擊事件的注冊btnId 與工具欄聲明里的 id 一一對應 editor.onButtonClick function(btnId) { if (btnId CustomTip) { // 在焦點位置插入一段業(yè)務提示文本 editor.insertHTML(此處為小編提示發(fā)布前請刪除); } };這個回調里能做很多事取選中內容、插入模板、彈窗選業(yè)務數(shù)據(jù)再回填。v8.0 對外暴露的編輯區(qū) API 不多但 insertHTML 和 getSelectHtml 這對組合已經(jīng)覆蓋了大部分內容插入類需求。寫回調時有一個細節(jié)如果自定義按鈕是彈出一個窗口讓用戶選擇數(shù)據(jù)彈窗打開前要先把編輯器當前的選中范圍記住否則窗口關閉后焦點回到編輯區(qū)原來的選中區(qū)域丟失插入位置會跑到文章末尾。常見做法是彈窗前用起始位置記錄偏移關閉后用 restore 之類的方式恢復。如果包版本不支持范圍恢復就把插入邏輯改成“始終在光標處插入”的簡化模式犧牲一點精度但穩(wěn)定。圖標缺失時按鈕不會出現(xiàn)在工具條上所以自制按鈕前先確認 images 目錄里沒有同名文件再自行放一張。放進去后強刷瀏覽器按鈕出現(xiàn)后再去綁回調避免把問題混在一起。5.2 配合業(yè)務從選中內容生成帶樣式的引用塊一個更真實的定制場景編輯要選中一段文字一鍵包成一個醒目的提示塊。這需要讀取選中內容并包進結構里。editor.onButtonClick function(btnId) { if (btnId CustomTip) { // 先取編輯區(qū)選中內容 var selHtml editor.getSelectHtml ? editor.getSelectHtml() : ; var tipHtml blockquote classeditor-tip (selHtml || 請輸入提示內容) /blockquote; editor.insertHTML(tipHtml); } };說明一下這里為什么先判斷 getSelectHtml 是否存在v8.0 不同小版本的 API 命名有出入直接用會報錯。寫擴展代碼時先做方法存在性判斷是和老版本 js 共存的習慣別嫌啰嗦它能避免很多“換個環(huán)境就掛”的詭異問題。插入的 HTML 最后要依賴 styleBody 里的 .editor-tip 樣式才能成型所以在第 3 章配置樣式時給自定義結構預留樣式類是配套操作不然插進去的是一段沒有樣式的裸標簽。樣式類的命名最好帶 editor- 前綴和業(yè)務樣式區(qū)分開這樣即使后期前臺改版也容易識別哪些是編輯器產(chǎn)物。多編輯器實例同時存在時回調里要避免用全局變量存 editor 對象。正確做法是讓每個實例自己的閉包持有引用function createCustomEditor(textareaId) { var editor new eWebEditor(textareaId, full); editor.BasePath /ewebeditor/; editor.onButtonClick function(btnId) { // 這里的 editor 是閉包內的實例不會串到另一個編輯器上 if (btnId CustomTip) { editor.insertHTML(自定義內容); } }; editor.create(); return editor; }這樣頁面里有多個編輯器時按鈕回調各自作用于自己的實例不會出現(xiàn)“編輯 A 文章時插到 B 文章末尾”的串場問題。5.3 上傳服務端安全改造一段可以直接抄的 PHP 示例第 4 章提到的 getshell 風險整改核心在服務端腳本。以 PHP 版為例我會把 upload.php 里最關鍵的環(huán)節(jié)改成下面這樣?php // 1. 白名單校驗擴展名第一次攔截 $allowExt array(jpg, jpeg, png, gif, bmp); $ext strtolower(pathinfo($_FILES[upload][name], PATHINFO_EXTENSION)); if (!in_array($ext, $allowExt)) { die({error:1,msg:類型不允許}); } // 2. 校驗真實文件類型防改名繞過 $finfo finfo_open(FILEINFO_MIME_TYPE); $mime finfo_file($finfo, $_FILES[upload][tmp_name]); finfo_close($finfo); if (!in_array($mime, array(image/jpeg, image/png, image/gif, image/bmp))) { die({error:1,msg:文件內容不是合法圖片}); } // 3. 重命名文件不保留原文件名目錄按日期隔離 $saveDir __DIR__ . /../../uploads/ . date(Ymd); if (!file_exists($saveDir)) { mkdir($saveDir, 0755, true); } $newName date(YmdHis) . _ . bin2hex(random_bytes(4)) . . . $ext; // 4. 移動成功后返回可訪問的 URL用站點常量拼完整地址 move_uploaded_file($_FILES[upload][tmp_name], $saveDir . / . $newName); echo {url: . SITE_URL . uploads/ . date(Ymd) . / . $newName . };這段代碼把前面討論過的三個核心點全占了擴展名白名單是第一道門真實類型識別是第二道門隨機重命名消除了用戶可控文件名日期目錄讓文件分布散開。SITE_URL 是項目已有的站點地址常量沒有就寫死在配置里不要用相對路徑拼。服務端文件類型校驗不是萬能的但能把大多數(shù)腳本后門擋在門外。配合 Web 服務器層把 uploads 目錄的腳本執(zhí)行權限關掉v8.0 最經(jīng)典的那條上傳漏洞路徑就被堵住了。Nginx 下可以對該目錄單獨配置 location 并禁掉 php 解析Apache 則用 Directory 配置 RemoveHandler 或 php_admin_flag。這一步在安全整改清單里屬于必做項不要因為編輯器看著不起眼就跳過歷史上因此失守的站點不少。6. 驗證與交付提交前內容清理與一條完整驗收路徑功能改完要交付了最后一步是給編輯器收口。收口的核心是“內容不能裸奔”用戶寫的 HTML 里可能帶著 script 和事件屬性前臺展示時會執(zhí)行。v8.0 不做內容過濾過濾必須落在提交函數(shù)里。function cleanHtml(html) { // 去掉 script、iframe、object 等標簽再去事件屬性 return html.replace(/script[^]*[\s\S]*?\/script/gi, ) .replace(/iframe[^]*[\s\S]*?\/iframe/gi, ) .replace(/object[\s\S]*?\/object/gi, ) .replace(/\son\w\s*\s*[][^]*[]/gi, ); } function beforeSubmit() { var html editor.contentHtml(); document.getElementById(content).value cleanHtml(html); return true; }這一層清理不能取代服務端過濾但能大幅減少前端風險標記的入庫。服務端建議再做一次同樣的清洗對存進庫的 HTML 只放行白名單標簽這是老系統(tǒng)加固的常見做法。驗收清單按這條路徑走一遍新建文章輸入帶格式內容并插入一張圖提交后到數(shù)據(jù)庫看源碼再在前臺頁面渲染確認圖片和樣式正常用管理員身份錄一段含 script 標簽的內容確認提交時被剝離換 Chrome 和 Edge 各驗證一次編輯、上傳、回寫三件事。v8.0 作為老版本在現(xiàn)在的瀏覽器上走的是兼容模式只要部署的是完整包、BasePath 正確主流瀏覽器都能用。我接手每個老 OA 項目時第一件事永遠是先把提交回寫和內容清理做掉再談別的需求。曾有一個站點因為漏了回寫編輯辛辛苦苦排的版保存后全部丟失那次教訓讓我把這一步焊進了流程。希望幫到你。本文還有配套的精品資源點擊獲取