Jackson对返回数据进行XSS过滤
定义一个类继承ObjectMapper
package demo;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.web.util.HtmlUtils;
import java.io.IOException;
public class CustomObjectMapper extends ObjectMapper {
private static final long serialVersionUID = -3448961813323784217L;
public CustomObjectMapper() {
SimpleModule module = new SimpleModule("HTML XSS Serializer",
new Version(1, 0, 0, "FINAL","com.yihaomen","ep-jsonmodule"));
module.addSerializer(new JsonHtmlXssSerializer(String.class));
this.registerModule(module);
}
class JsonHtmlXssSerializer extends JsonSerializer<String> {
public JsonHtmlXssSerializer(Class<String> string) {
super();
}
@Override
public Class<String> handledType() {
return String.class;
}
@Override
public void serialize(String value, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException,
JsonProcessingException {
if (value != null) {
String encodedValue = HtmlUtils.htmlEscape(value.toString());
jsonGenerator.writeString(encodedValue);
}
}
}
}
在Spring配置文件中进行配置
<mvc:annotation-driven>
<mvc:message-converters>
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
<property name="objectMapper">
<bean class="demo.CustomObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
添加测试Controller,验证上面配置是否成功
@RequestMapping("/testXSS")
@ResponseBody
public UserDTO testXSS() {
UserDTO userDTO = new UserDTO();
userDTO.setFullName("<script>alert(1)</script>");
return userDTO;
}
用Postman测试接口,可以看到,fullName字段的数据已经过html编码过滤了
以上是对jackson返回数据进行xss过滤的配置,下面是我自己通过java代码实现的一种方法
JAVA反射实现XSS过滤
package demo;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.util.HtmlUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author zhangkun
* @date 2020/3/10 11:34
*/
public class XssUtil {
public static void xssObject(Object obj) {
// 获取返回对象类所有字段
List<Field> fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields()));
// 获取返回对象的父类的所有字段,(如果有的话,根据自己情况删减)
List<Field> parentFields = new ArrayList<>(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields()));
// 合并父类字段和本类字段(如果有的话,根据自己情况删减)
fields.addAll(parentFields);
// 维护一个以转义的名单列表,避免同一字段重复被转义,因为父类和子类可能存在相同的字段名(没有父类的情况可删减)
List<String> nameList = new ArrayList<>();
for (Field field : fields) { //遍历所有属性
String type = field.getGenericType().getTypeName();
String name = field.getName(); //获取属性的名字
if (type.equals(String.class.getTypeName()) && !nameList.contains(name)) {
// 如果使用到了这个字段,则添加到名单中,以免下次重复被使用
nameList.add(name);
// 根据javaBean规范,set或get方法字段的首字母大写
name = name.substring(0, 1).toUpperCase() + name.substring(1);
Method getMethod = null;
Method setMethod = null;
try {
// 获取本类的get、set方法
getMethod = obj.getClass().getMethod("get" + name);
setMethod = obj.getClass().getMethod("set" + name, new Class[]{String.class});
} catch (NoSuchMethodException e) {
try {
// 如果没有获取到则去父类获取(如果有的话,根据自己情况删减)
getMethod = obj.getClass().getSuperclass().getMethod("get" + name);
setMethod = obj.getClass().getSuperclass().getMethod("set" + name, new Class[]{String.class});
} catch (NoSuchMethodException e1) {
}
}
if (getMethod != null && setMethod != null) {
String value = null;
try {
// 通过get方法获取到值
String value = (String) getMethod.invoke(obj);
if (StringUtils.isBlank(value)) {
continue;
}
// 对字段进行html编码,然后通过set方法再塞回去
String s = HtmlUtils.htmlEscape(value);
setMethod.invoke(obj, s);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
}
测试Controller
@RequestMapping("/testXSS")
@ResponseBody
public UserDTO testXSS() {
UserDTO userDTO = new UserDTO();
userDTO.setFullName("<script>alert("佛山馆所属管'")</script>");
// 使用我自己写的XSS过滤工具类
XssUtil.xssObject(userDTO);
return userDTO;
}
Postman测试返回结果
大功告成!