如何在React.js博客的textarea中安全实现文本内可点击链接功能
React博客安全实现可点击链接的方案
推荐三种安全可行的实现方式,按安全程度和易用性排序如下:
1. 自定义标记语法解析(最安全)
让用户使用类似 [链接文本](目标URL) 的轻量标记语法输入链接,然后自己编写解析函数将文本转换为React元素,完全避免使用dangerouslySetInnerHTML,从根源杜绝代码注入风险。
代码示例
import { useState } from 'react'; // 解析带自定义标记的文本,生成React元素数组 const parseTextWithLinks = (text) => { // 匹配 [文本](https://xxx) 格式的正则 const linkRegex = /\[([^\]]+)\]\((https?:\/\/[^\)]+)\)/g; const parts = []; let lastIndex = 0; let match; while ((match = linkRegex.exec(text)) !== null) { // 添加链接前的普通文本 if (match.index > lastIndex) { parts.push(text.slice(lastIndex, match.index)); } // 添加安全的链接元素 parts.push( <a key={match.index} href={match[2]} target="_blank" rel="noopener noreferrer" style={{ color: '#0066cc', textDecoration: 'underline' }} > {match[1]} </a> ); lastIndex = linkRegex.lastIndex; } // 添加剩余的普通文本 if (lastIndex < text.length) { parts.push(text.slice(lastIndex)); } return parts; }; const BlogEditor = () => { const [content, setContent] = useState(''); return ( <div> <textarea value={content} onChange={(e) => setContent(e.target.value)} placeholder="使用 [链接文本](URL) 格式插入链接" rows={10} style={{ width: '100%' }} /> <div style={{ marginTop: '20px', padding: '10px', border: '1px solid #eee' }}> {parseTextWithLinks(content)} </div> </div> ); }; export default BlogEditor;
2. DOMPurify 配合 dangerouslySetInnerHTML
如果需要允许用户直接输入HTML格式的链接(比如<a href="xxx">文本</a>),必须先通过HTML净化库过滤恶意代码,再使用dangerouslySetInnerHTML渲染。
代码示例
import { useState } from 'react'; import DOMPurify from 'dompurify'; const BlogEditor = () => { const [content, setContent] = useState(''); // 严格净化用户输入,只保留允许的标签和属性 const sanitizedContent = DOMPurify.sanitize(content, { ADD_TAGS: ['a'], ADD_ATTR: ['href'], FORBID_ATTR: ['onclick', 'onload', 'onerror'], ALLOWED_PROTOCOLS: ['http', 'https'] }); return ( <div> <textarea value={content} onChange={(e) => setContent(e.target.value)} placeholder="直接输入 <a href='URL'>链接文本</a> 格式的内容" rows={10} style={{ width: '100%' }} /> <div style={{ marginTop: '20px', padding: '10px', border: '1px solid #eee' }} dangerouslySetInnerHTML={{ __html: sanitizedContent }} /> </div> ); }; export default BlogEditor;
3. 使用成熟的富文本编辑器
直接集成现成的富文本编辑器组件(如Draft.js、Slate),这类组件会将内容以结构化JSON格式存储,渲染时直接转换为安全的React元素,用户通过UI按钮插入链接,无需手动输入标记或HTML,体验和安全性兼顾。
简化示例(Draft.js)
import { useState } from 'react'; import { Editor, EditorState, RichUtils } from 'draft-js'; import 'draft-js/dist/Draft.css'; const BlogEditor = () => { const [editorState, setEditorState] = useState(EditorState.createEmpty()); // 触发插入链接的UI逻辑 const handleInsertLink = () => { const url = prompt('请输入链接地址:'); if (!url) return; const contentState = editorState.getCurrentContent(); const selection = editorState.getSelection(); const newContentState = RichUtils.toggleLink( contentState, selection, url ); setEditorState(EditorState.push(editorState, newContentState, 'toggle-link')); }; return ( <div> <button onClick={handleInsertLink} style={{ marginBottom: '10px' }}> 插入链接 </button> <Editor editorState={editorState} onChange={setEditorState} placeholder="撰写博客内容,点击按钮插入链接" style={{ border: '1px solid #eee', padding: '10px', minHeight: '200px' }} /> </div> ); }; export default BlogEditor;
方案对比
- 自定义标记解析:安全性最高,完全可控,适合需求简单的场景,但需要自行维护解析逻辑。
- DOMPurify方案:适合允许HTML输入的场景,通过严格规则降低风险,但依赖第三方库。
- 富文本编辑器:用户体验最好,内置安全处理,适合复杂编辑需求,但会增加项目体积。
内容的提问来源于stack exchange,提问作者MD Sajed
相关产品推荐
相关产品推荐

