本文目录导读:

我来为您详细解释 JEditorPane、HTMLEditorKit 和 ParserMethodFilter 在 Java Swing 中的使用。
这三个组件通常一起使用来实现 HTML 内容的解析和过滤功能:
- JEditorPane:Swing 中的 HTML 显示组件
- HTMLEditorKit:负责 HTML 的编辑和显示
- ParserMethodFilter:用于过滤 HTML 解析过程中的特定元素
基本使用示例
import javax.swing.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
public class HTMLFilterExample {
public static void main(String[] args) {
JFrame frame = new JFrame("HTML Filter Example");
JEditorPane editorPane = new JEditorPane();
// 创建并配置 HTMLEditorKit
HTMLEditorKit kit = new HTMLEditorKit();
// 设置自定义的 ParserMethodFilter
HTMLParser parser = new HTMLParser() {
@Override
protected HTMLParserCallback createParserCallback() {
return new HTMLParserCallback() {
@Override
public void handleStartTag(TagElement tag, MutableAttributeSet attrs) {
if (!shouldFilter(tag)) {
super.handleStartTag(tag, attrs);
}
}
@Override
public void handleEndTag(TagElement tag) {
if (!shouldFilter(tag)) {
super.handleEndTag(tag);
}
}
private boolean shouldFilter(TagElement tag) {
String tagName = tag.getHTMLTag().toString();
// 过滤特定的标签
return tagName.equals("script") ||
tagName.equals("iframe");
}
};
}
};
// 应用设置
kit.setParser(parser);
editorPane.setEditorKit(kit);
// 加载 HTML 内容
String html = "<html><body>" +
"<h1>Hello World</h1>" +
"<script>alert('XSS');</script>" +
"<p>Safe content</p>" +
"</body></html>";
editorPane.setText(html);
editorPane.setEditable(false);
frame.add(new JScrollPane(editorPane));
frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
自定义 ParserMethodFilter
public class CustomParserMethodFilter {
// 使用 ParserMethodFilter 过滤特定 HTML 元素
public static void applyFilter(JEditorPane editorPane) {
HTMLEditorKit kit = (HTMLEditorKit) editorPane.getEditorKit();
// 创建过滤器
ParserMethodFilter filter = new ParserMethodFilter() {
@Override
public boolean acceptMethod(String methodName) {
// 只允许特定的解析方法
return methodName.equals("handleStartTag") ||
methodName.equals("handleEndTag") ||
methodName.equals("handleSimpleTag") ||
methodName.equals("handleText");
}
@Override
public boolean acceptParameter(String methodName,
Object[] parameters) {
// 对特定参数进行过滤
if (methodName.equals("handleStartTag")) {
if (parameters.length > 0 &&
parameters[0] instanceof HTML.Tag) {
HTML.Tag tag = (HTML.Tag) parameters[0];
// 阻止 script 标签的解析
return !tag.equals(HTML.Tag.SCRIPT);
}
}
return true;
}
};
// 应用过滤器
// 注意:这里需要扩展 HTMLEditorKit 或使用反射机制
}
}
完整的高级示例
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
import java.awt.*;
import java.io.*;
import java.net.*;
public class AdvancedHTMLFilterDemo extends JFrame {
private JEditorPane editorPane;
private JTextArea logArea;
public AdvancedHTMLFilterDemo() {
setTitle("HTML Filter Demo");
setLayout(new BorderLayout());
// 创建编辑器面板
editorPane = new JEditorPane();
editorPane.setContentType("text/html");
// 自定义 HTMLEditorKit
CustomHTMLKit kit = new CustomHTMLKit();
editorPane.setEditorKit(kit);
editorPane.setEditable(false);
// 日志面板
logArea = new JTextArea(5, 40);
logArea.setEditable(false);
// 控制按钮
JButton loadButton = new JButton("Load HTML");
loadButton.addActionListener(e -> loadSampleHTML());
// 布局
add(new JScrollPane(editorPane), BorderLayout.CENTER);
add(new JScrollPane(logArea), BorderLayout.SOUTH);
add(loadButton, BorderLayout.NORTH);
setSize(600, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void loadSampleHTML() {
String html = "<html>\n" +
"<head><title>Test</title></head>\n" +
"<body>\n" +
" <h1>Safe Content</h1>\n" +
" <p>This is safe paragraph</p>\n" +
" <script>alert('Blocked!');</script>\n" +
" <a href='https://example.com'>Link</a>\n" +
" <iframe src='https://evil.com'></iframe>\n" +
"</body>\n" +
"</html>";
editorPane.setText(html);
}
// 自定义 HTMLEditorKit
class CustomHTMLKit extends HTMLEditorKit {
@Override
public Document createDefaultDocument() {
HTMLDocument doc = new HTMLDocument() {
@Override
public HTMLEditorKit.ParserCallback getReader(int pos) {
return new FilteredHTMLReader(pos);
}
};
return doc;
}
private class FilteredHTMLReader extends HTMLDocument.HTMLReader {
public FilteredHTMLReader(int offset) {
super(offset);
}
@Override
public void handleStartTag(TagElement t,
MutableAttributeSet a,
int pos) {
String tagName = t.getHTMLTag().toString();
// 过滤不安全标签
if (tagName.equals("script") || tagName.equals("iframe")) {
logArea.append("Filtered out: <" + tagName + ">\n");
return; // 跳过该标签
}
// 过滤特定属性
if (tagName.equals("a") || tagName.equals("img")) {
String src = (String) a.getAttribute(HTML.Attribute.SRC);
String href = (String) a.getAttribute(HTML.Attribute.HREF);
if (src != null && src.startsWith("javascript:")) {
logArea.append("Blocked javascript in src\n");
return;
}
if (href != null && href.startsWith("javascript:")) {
logArea.append("Blocked javascript in href\n");
return;
}
}
super.handleStartTag(t, a, pos);
}
@Override
public void handleSimpleTag(TagElement t,
MutableAttributeSet a,
int pos) {
String tagName = t.getHTMLTag().toString();
if (tagName.equals("img")) {
String src = (String) a.getAttribute(HTML.Attribute.SRC);
if (src != null && !isValidImageSource(src)) {
logArea.append("Blocked image: " + src + "\n");
return;
}
}
super.handleSimpleTag(t, a, pos);
}
private boolean isValidImageSource(String src) {
return !src.startsWith("data:") ||
src.startsWith("data:image/");
}
}
}
// 更高级的过滤器 - 使用 ParserMethodFilter
class SecurityFilter implements ParserMethodFilter {
@Override
public boolean acceptMethod(String methodName) {
// 允许的解析方法
String[] allowedMethods = {
"handleStartTag", "handleEndTag", "handleSimpleTag",
"handleText", "handleComment", "handleError"
};
for (String method : allowedMethods) {
if (method.equals(methodName)) {
return true;
}
}
return false;
}
@Override
public boolean acceptParameter(String methodName,
Object[] parameters) {
// 对特定方法进行参数验证
if (methodName.equals("handleStartTag")) {
if (parameters.length >= 1 &&
parameters[0] instanceof TagElement) {
TagElement tag = (TagElement) parameters[0];
String tagName = tag.getHTMLTag().toString();
// 黑名单标签
String[] blacklist = {
"script", "iframe", "object", "embed", "applet"
};
for (String blocked : blacklist) {
if (tagName.equals(blocked)) {
logArea.append("Blocked tag: " + tagName + "\n");
return false;
}
}
}
}
// 检查属性中的潜在危险内容
if (methodName.equals("handleStartTag") ||
methodName.equals("handleSimpleTag")) {
if (parameters.length >= 2 &&
parameters[1] instanceof MutableAttributeSet) {
MutableAttributeSet attrs =
(MutableAttributeSet) parameters[1];
// 检查事件处理器
Enumeration<?> attrNames = attrs.getAttributeNames();
while (attrNames.hasMoreElements()) {
Object attrName = attrNames.nextElement();
if (attrName instanceof HTML.Attribute) {
HTML.Attribute attr = (HTML.Attribute) attrName;
if (attr.toString().startsWith("on")) {
logArea.append("Blocked event handler: " +
attr + "\n");
return false;
}
}
}
}
}
return true;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new AdvancedHTMLFilterDemo().setVisible(true);
});
}
}
使用 ParserMethodFilter 的安全过滤
import javax.swing.text.html.parser.*;
public class SecureHTMLParser extends DocumentParser {
private ParserMethodFilter filter;
public SecureHTMLParser() {
this.filter = new DefaultFilter();
}
public SecureHTMLParser(ParserMethodFilter filter) {
this.filter = filter;
}
@Override
protected void handleStartTag(TagElement tag) {
if (filter.acceptMethod("handleStartTag")) {
Object[] params = {tag, getAttributes()};
if (filter.acceptParameter("handleStartTag", params)) {
super.handleStartTag(tag);
} else {
logWarning("Start tag filtered: " + tag.getHTMLTag());
}
}
}
@Override
protected void handleEndTag(TagElement tag) {
if (filter.acceptMethod("handleEndTag")) {
super.handleEndTag(tag);
}
}
private void logWarning(String message) {
System.err.println("[Security] " + message);
}
// 默认过滤器实现
static class DefaultFilter implements ParserMethodFilter {
@Override
public boolean acceptMethod(String methodName) {
return true; // 默认接受所有方法
}
@Override
public boolean acceptParameter(String methodName,
Object[] parameters) {
return true; // 默认接受所有参数
}
}
}
使用建议
- 性能考虑:过滤操作会增加解析时间,建议在需要时启用
- 安全级别:根据应用需求选择合适的过滤策略
- 兼容性:确保过滤后的 HTML 仍然保持正确的结构和显示
- 错误处理:做好过滤异常的处理和日志记录
这个示例展示了如何使用 JEditorPane、HTMLEditorKit 和 ParserMethodFilter 来实现 HTML 内容的安全过滤和自定义解析。