需求背景
post请求中的body参数,会解析成 @RequestBody 注解的对象。现在需要把该对象的所有string属性进行XSS过滤,XSS过滤是已有的轮子。
设计思路
- 面向扩展开放,面向修改关闭原则。新定义一个注解解决这类问题:@XssCleaned
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XssCleaned {
}
实现
- 自定义扩展HandlerMethodArgumentResolver,由于需要替代@RequestBody,解析MediaType=application/*+json的请求中的body参数 (参见MappingJackson2HttpMessageConverter) 所以使用的自定义扩展HandlerMethodArgumentResolver的子类RequestResponseBodyMethodProcessor。
public class XssCleanedResolver extends RequestResponseBodyMethodProcessor{
public XssCleanedResolver(List<HttpMessageConverter<?>> converters) {
super(converters);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(XssCleaned.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object o = super.resolveArgument(parameter,mavContainer,webRequest,binderFactory);
//将所有string类型的feild进行xss过滤
PostBodyUtil.cleanXSSfields(o);
return o;
}
}
- 遍历string属性,进行过滤
public class PostBodyUtil {
/**
* 所有string类型的字段都进行xss过滤
* @return
*/
public static boolean cleanXSSfields(Object o){
Arrays.stream(BeanUtils.getPropertyDescriptors(o.getClass())).forEach(t->{
if (t.getPropertyType().equals(String.class)){
String fieldValue = "";
try {
fieldValue = (String) t.getReadMethod().invoke(o);
t.getWriteMethod().invoke(o,
AntiXssUtil.decodeHtml(AntiXssUtil.cleanHtml(fieldValue)));
} catch (IllegalAccessException | InvocationTargetException e) {
LoggerHandler.error(e, "cleanHtml failed!"+t.getName()+":"+fieldValue);
return;
}
}
}
);
return true;
}
}
- 注册该resolver 在自定义扩展的WebMvcConfigurationSupport中,重写addArgumentResolvers,将该resolver注册。
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new XssCleanedResolver(converters));
}
- 使用注解实例
@PostMapping("/updateShipments")
public PlatformReturn updateShipments(@XssCleaned Shipment shipment, @CurrentUser SysUser user){
return myOrderService.updateShipments(shipment,user);
}
这样,shipment对象的每个string属性都是经过xss过滤的,其它功能与 @RequestBody 功能相同。
over