用自定义注解替代@RequestBody实现xss过滤的尝试

2,885 阅读1分钟

需求背景

post请求中的body参数,会解析成 @RequestBody 注解的对象。现在需要把该对象的所有string属性进行XSS过滤,XSS过滤是已有的轮子。

设计思路

  1. 面向扩展开放,面向修改关闭原则。新定义一个注解解决这类问题:@XssCleaned
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XssCleaned {
}

实现

  1. 自定义扩展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;
   }

}
  1. 遍历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;
   }
}
  1. 注册该resolver 在自定义扩展的WebMvcConfigurationSupport中,重写addArgumentResolvers,将该resolver注册。
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(new XssCleanedResolver(converters));
}
  1. 使用注解实例
@PostMapping("/updateShipments")
public PlatformReturn updateShipments(@XssCleaned Shipment shipment, @CurrentUser SysUser user){

    return myOrderService.updateShipments(shipment,user);
}

这样,shipment对象的每个string属性都是经过xss过滤的,其它功能与 @RequestBody 功能相同。

over