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測試返回結果
大功告成!