Struts2自定义JSON Result全局转义字符串防范XSS漏洞方案问询
全局自定义JSON Result解决XSS漏洞方案
完全可以通过自定义全局JSON Result实现所有字符串序列化前自动转义,无需修改现有业务Action代码,是目前场景下的最优解,具体实现步骤如下:
1. 实现XSS转义工具
public class XssEscaper { public static String escape(String input) { if (input == null) { return null; } // 也可以直接用commons-lang的StringEscapeUtils.escapeHtml实现 return input.replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("\"", """) .replace("'", "'"); } }
2. 自定义继承原生JSONResult的安全实现
重写序列化逻辑,递归转义所有字符串类型的字段值:
import com.opensymphony.xwork2.ActionInvocation; import org.apache.struts2.json.JSONResult; import java.lang.reflect.Field; import java.util.Collection; import java.util.Map; public class XssSafeJsonResult extends JSONResult { @Override protected String serialize(Object o) throws Exception { Object escapedObj = recursiveEscape(o); return super.serialize(escapedObj); } private Object recursiveEscape(Object obj) throws IllegalAccessException { if (obj == null) { return null; } // 字符串直接转义 if (obj instanceof String) { return XssEscaper.escape((String) obj); } // 遍历集合元素转义 if (obj instanceof Collection) { for (Object item : (Collection<?>) obj) { recursiveEscape(item); } return obj; } // 遍历Map值转义 if (obj instanceof Map) { for (Map.Entry<?, ?> entry : ((Map<?, ?>) obj).entrySet()) { recursiveEscape(entry.getValue()); } return obj; } // 遍历自定义对象所有字段转义 Class<?> clazz = obj.getClass(); while (clazz != Object.class) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object fieldValue = field.get(obj); Object escapedValue = recursiveEscape(fieldValue); if (fieldValue != escapedValue) { field.set(obj, escapedValue); } } clazz = clazz.getSuperclass(); } return obj; } }
3. 替换全局JSON结果类型
在struts.xml配置中覆盖原有json结果类型,全局生效:
<package name="default" extends="json-default"> <result-types> <result-type name="json" class="你的自定义类包路径.XssSafeJsonResult" default="true"/> </result-types> <!-- 原有其他配置保持不变 --> </package>
基于Jackson的简化实现(如果项目用Jackson做JSON序列化)
如果项目JSON序列化底层采用Jackson,可通过全局注册字符串序列化器实现,逻辑更简洁:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import java.io.IOException; public class XssStringSerializer extends JsonSerializer<String> { @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(XssEscaper.escape(value)); } } // 全局ObjectMapper配置处添加如下代码即可 SimpleModule xssModule = new SimpleModule("XssEscapeModule"); xssModule.addSerializer(String.class, new XssStringSerializer()); objectMapper.registerModule(xssModule);
可选扩展
如果部分接口需要返回富文本等不需要转义的内容,可以自定义@NoXssEscape注解,在转义逻辑中判断字段/类带有该注解时跳过转义即可。
内容的提问来源于stack exchange,提问作者Alireza Fattahi
相关产品推荐
相关产品推荐

