很多时候我们都知道,xss,csrf都需要通过我们前台传入的数据,然后再输出到页面,渲染成可执行脚本,导致加载页面即可执行或者被动型的让用户点击各种常用的按钮来触发
脚本效果,所以我们需要严格筛选以及控制过滤数据对象的各个属性字段值,我相信很多人都用validator,但是我感觉这样可订制的灵活性是比较低的,然后我自己就想设计一个可插拔式,可订制的校验器;当我们的普通validator不再满足到数据筛选的时候,可在第二重校验器实现我们的数据过滤
我就不啰嗦为何要设计的背景了,下面我就帖代码说明下自己的设计思路
1.写个总的校验器接口,利用泛型规定返回视图类型,还有校验对象的类型
package com.silvery.plugin.validator; /** * 数据校验器接口 * * @author shadow * * @param <R> * 视图类型 * @param <T> * 校验对象 */ public interface FormValidator<R, T> { public R validate(T t) throws FormValidateException; }
2.定义一个返回视图的对象
package com.silvery.plugin.validator; import java.util.Map; /** * 数据校验结果视图 * * @author shadow * */ public class FormValidateResult { private boolean success; // true=成功;false=失败 private Map<String, Object> errorMap; // 失败对应信息 public FormValidateResult() { this(false); } public FormValidateResult(boolean success) { this.success = success; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public Map<String, Object> getErrorMap() { return errorMap; } public void setErrorMap(Map<String, Object> errorMap) { this.errorMap = errorMap; } }
3.写一个专门执行校验器的执行类
package com.silvery.plugin.validator; import java.lang.reflect.Method; import java.util.List; import com.silvery.utils.ReflectUtils; /** * * 数据校验器执行类 * * @author shadow * */ public class SimpleFormValidator<T> { /** * 表单校验方法 * * @param t * 校验数据对象 * @return 数据校验视图 * @throws FormValidateException * 校验异常 */ public FormValidateResult validate(T t) throws FormValidateException { return validate(null, t); } /** * 表单校验方法 * * @param validator * 用户自定义校验器 * @param t * 校验数据对象 * @return 数据校验视图 * @throws FormValidateException * 校验异常 */ public FormValidateResult validate(FormValidator<FormValidateResult, T> validator, T t) throws FormValidateException { FormValidateResult validateResult = new FormValidateResult(); // 优先执行用户自定义校验器,如不通过则跳出 if (validator != null) { validateResult = validator.validate(t); if (!validateResult.isSuccess()) { return validateResult; } } return validateForXSS(t); } /** 校验是否存在XSS攻击脚本,当前只检测String类型声明字段 */ private FormValidateResult validateForXSS(T t) { List<Method> methods = ReflectUtils.getMethods(t.getClass()); for (Method method : methods) { if (method.getName().startsWith("set")) { Class<?>[] parameterClass = method.getParameterTypes(); // 参数不能为null, 参数只有一个, 参数类型只能是String if (parameterClass != null && parameterClass.length == 1 && parameterClass[0].equals(String.class)) { // ReflectUtils.writeValueByMethod(t, method, "test"); } } } return new FormValidateResult(true); } }
4. 实现我们的校验器接口,自己写所需逻辑过滤
package com.silvery.project.cms.validator; import java.util.HashMap; import java.util.Map; import com.silvery.plugin.validator.FormValidateException; import com.silvery.plugin.validator.FormValidateResult; import com.silvery.plugin.validator.FormValidator; import com.silvery.project.cms.model.Authority; /** * 权限实体数据校验器实现 * * @author shadow * */ public class AuthorityFormValidator implements FormValidator<FormValidateResult, Authority> { @Override public FormValidateResult validate(Authority t) throws FormValidateException { // TODO do some validate operate FormValidateResult formValidateResult = new FormValidateResult(); Map<String, Object> errorFieldMap = new HashMap<String, Object>(); formValidateResult.setSuccess(true); formValidateResult.setErrorMap(errorFieldMap); return formValidateResult; } }
4. 工作都准备得差不多的话,就开始接入到我的业务
/** 校验对象数据合法性 */ protected ServiceResult<T> validateForm(ServiceResult<T> serviceResult, T t) throws FormValidateException { FormValidateResult formValidateResult = new SimpleFormValidator<T>().validate(initFormValidator(), t); if (formValidateResult.isSuccess()) { return serviceResult.setSuccess(true); } else { return formValidateResult2ViewResult(serviceResult, formValidateResult); } }
@Override public ServiceResult<T> insert(T model) { ServiceResult<T> serviceResult = createServiceResult(); try { serviceResult = validateForm(serviceResult, model); if (!serviceResult.isSuccess()) { return serviceResult; } int count = getDefaultDao().insert(model); if (count > 0) { serviceResult.setSuccess(true).setMessage( new StringBuffer("成功保存[").append(count).append("]条记录").toString()); } else { serviceResult.setMessage("没有保存到任何记录"); } } catch (Exception e) { logErrorResult(serviceResult, e); } return serviceResult; }
后语, 我也没有写明确的xss,csrf过滤规则,我只是提供一个可无缝接入业务的校验器实现,可以让你动态修改里面的数据,或者是拦截数据,希望对大家有帮助