SpringBoot 通过Mybatis 拦截器 实现HTML标签转义

熊孩纸 阅读:988 2021-03-31 16:50:44 评论:0

说到注入攻击(xss攻击),我看到baidu和google 上的实现方式,主要是分为以下几种:

第一种:通过Filter 过滤器转义请求参数携带的特殊字符(注意POST 和GET请求参数方法不一样)

第二种:通过配置SpringMVC 的MessageConverter<T>过滤请求参数携带的特殊字符

第三种:通过Mybatis 拦截器,拦截所有的insert和update 操作(请求参数特殊字符的转义),拦截所有的select 操作(查询 结果的反转)

重点讲解第三种实现方式:

核心功能代码:

 
 
import java.lang.reflect.Field; 
import java.util.Properties; 
import org.apache.ibatis.executor.Executor; 
import org.apache.ibatis.mapping.MappedStatement; 
import org.apache.ibatis.mapping.SqlCommandType; 
import org.apache.ibatis.plugin.Interceptor; 
import org.apache.ibatis.plugin.Intercepts; 
import org.apache.ibatis.plugin.Invocation; 
import org.apache.ibatis.plugin.Plugin; 
import org.apache.ibatis.plugin.Signature; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.web.util.HtmlUtils; 
 
/** 
 * 加码特殊字符转义(html) 
 * 
 */ 
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) 
public class EncodeInterceptor implements Interceptor { 
	// 日志记录 
	public static final Logger log = LoggerFactory.getLogger(EncodeInterceptor.class); 
	// 编码HTML(默认不开启) 
	private boolean escapeHTML = false; 
 
	// set 和 get 方法 
	public boolean isEscapeHTML() { 
		return escapeHTML; 
	} 
 
	public void setEscapeHTML(boolean escapeHTML) { 
		this.escapeHTML = escapeHTML; 
	} 
 
	// 构造函数 
	public EncodeInterceptor() { 
		super(); 
	} 
 
	public EncodeInterceptor(boolean escapeHTML) { 
		super(); 
		this.escapeHTML = escapeHTML; 
	} 
 
	@Override 
	public Object intercept(Invocation invocation) throws Throwable { 
		MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; 
 
		// 获取 SQL 
		SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); 
 
		// 获取参数 
		Object parameter = invocation.getArgs()[1]; 
		 
		if(this.escapeHTML){ 
			// 获取私有成员变量 
			Field[] declaredFields = parameter.getClass().getDeclaredFields(); 
			 
			for (Field field : declaredFields) { 
				// 判断MySQL数据库操作:insert 或者 update 
				if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { 
					// 判断是否开启--HTML转义(String 类型转义) 
						if(field.getType() == String.class ){ 
							// 无视private/protected修饰符, 不经过setter函数. 
							field.setAccessible(true); 
							try { 
								String value = (String)field.get(parameter); 
								field.set(parameter, HtmlUtils.htmlEscape(value)); 
							} catch (IllegalAccessException e) { 
								log.error(e.getMessage()); 
							} 
						} 
					 
				} 
			} 
		} 
 
 
		return invocation.proceed(); 
	} 
 
	@Override 
	public Object plugin(Object target) { 
		// TODO Auto-generated method stub 
		return Plugin.wrap(target, this); 
 
	} 
 
	@Override 
	public void setProperties(Properties properties) { 
		// TODO Auto-generated method stub 
 
	} 
 
} 
 
import java.lang.reflect.Field; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Properties; 
import org.apache.commons.collections.CollectionUtils; 
import org.apache.commons.lang.StringUtils; 
import org.apache.ibatis.cache.CacheKey; 
import org.apache.ibatis.executor.Executor; 
import org.apache.ibatis.mapping.BoundSql; 
import org.apache.ibatis.mapping.MappedStatement; 
import org.apache.ibatis.mapping.SqlCommandType; 
import org.apache.ibatis.plugin.Interceptor; 
import org.apache.ibatis.plugin.Intercepts; 
import org.apache.ibatis.plugin.Invocation; 
import org.apache.ibatis.plugin.Plugin; 
import org.apache.ibatis.plugin.Signature; 
import org.apache.ibatis.session.ResultHandler; 
import org.apache.ibatis.session.RowBounds; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.web.util.HtmlUtils; 
import com.digipower.ucas.domain.BusForm; 
import com.digipower.ucas.vo.BusFormWrapper; 
 
/** 
 * 转义字符->特殊字符 
 * 
 */ 
@Intercepts({  
	@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,RowBounds.class, ResultHandler.class }), 
	@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }) 
}) 
public class DecodeInterceptor implements Interceptor { 
 
	// 日志记录 
	public static final Logger log = LoggerFactory.getLogger(EncodeInterceptor.class); 
	// 编码HTML(默认不开启) 
	private boolean escapeHTML = false; 
	 
	// 拦截指定Mapper 
	private List<String> mappers; 
 
	// set 和 get 方法 
	public boolean isEscapeHTML() { 
		return escapeHTML; 
	} 
 
	public void setEscapeHTML(boolean escapeHTML) { 
		this.escapeHTML = escapeHTML; 
	} 
	 
	public List<String> getMappers() { 
		return mappers; 
	} 
 
	public void setMappers(List<String> mappers) { 
		this.mappers = mappers; 
	} 
 
	// 构造函数 
	public DecodeInterceptor() { 
		super(); 
	} 
 
	public DecodeInterceptor(boolean escapeHTML) { 
		super(); 
		this.escapeHTML = escapeHTML; 
	} 
	 
	public DecodeInterceptor(boolean escapeHTML, List<String> mappers) { 
		super(); 
		this.escapeHTML = escapeHTML; 
		this.mappers = mappers; 
	} 
 
	@Override 
	public Object intercept(Invocation invocation) throws Throwable { 
		// 拦截特定的查询mapper 
				MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; 
				String mapper = mappedStatement.getId(); 
				// 获取 SQL 
				SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); 
				if (SqlCommandType.SELECT.equals(sqlCommandType)) { 
			 
						Object result = invocation.proceed(); 
						// 判断返回集合是否为array 
						String name = result.getClass().getName(); 
						if(!StringUtils.isEmpty(name) && "java.util.ArrayList".equalsIgnoreCase(name)){ 
							ArrayList<Object> list = (ArrayList<Object>)result; 
							if(list != null && list.size() > 0){ 
								for(Object obj : list){ 
									// 获取私有成员变量 
									Class<?> clazz = obj.getClass(); 
									for (; clazz != Object.class; clazz = clazz.getSuperclass()) {//向上循环  遍历父类 
										Field[] fields = clazz.getDeclaredFields(); 
										for (Field field : fields) { 
											field.setAccessible(true); 
											// 判断是否开启--HTML反转(String 类型转义) 
											if (field.getType() == String.class) { 
												// 无视private/protected修饰符, 不经过setter函数. 
													field.setAccessible(true); 
													try { 
														String value = (String) field.get(obj); 
														field.set(obj, HtmlUtils.htmlUnescape(value)); 
													} catch (IllegalAccessException e) { 
														log.error(e.getMessage()); 
													} 
											} 
											 
										} 
									} 
								} 
							} 
							return list; 
							 
						} else { 
							// 获取私有成员变量 
							Class<?> clazz = result.getClass(); 
							for (; clazz != Object.class; clazz = clazz.getSuperclass()) {//向上循环  遍历父类 
								Field[] fields = clazz.getDeclaredFields(); 
								for (Field field : fields) { 
									field.setAccessible(true); 
									// 判断是否开启--HTML反转(String 类型转义) 
									if (field.getType() == String.class) { 
										// 无视private/protected修饰符, 不经过setter函数. 
											field.setAccessible(true); 
											try { 
												String value = (String) field.get(result); 
												field.set(result, HtmlUtils.htmlUnescape(value)); 
											} catch (IllegalAccessException e) { 
												log.error(e.getMessage()); 
											} 
									} 
									 
								} 
							} 
							return result; 
						} 
				} 
 
		return invocation.proceed(); 
	} 
 
	@Override 
	public Object plugin(Object target) { 
		// TODO Auto-generated method stub 
		return Plugin.wrap(target, this); 
	} 
 
	@Override 
	public void setProperties(Properties properties) { 
		// TODO Auto-generated method stub 
	} 
 
} 

SpringBoot 涉及MyBatis 配置文件:

@Configuration 
public class MyBatisConfig { 
 
	 
	/** 
	 * mybatis 自定义拦截器 
	 * @return 
	 */ 
	@Bean 
	public DecodeInterceptor getDecodeInterceptor(@Value("${escapeHTML}") boolean escapeHTML, @Value("${unescapeHTML}") String unescapeHTML){ 
		DecodeInterceptor interceptor = null; 
		if(StringUtils.isEmpty(unescapeHTML)){ 
			interceptor = new DecodeInterceptor(escapeHTML); 
		}else { 
			List<String> mappers = null; 
			if(unescapeHTML.contains(",")){ 
				mappers = Arrays.asList(unescapeHTML.split(",")); 
			} else { 
				mappers = Arrays.asList(new String[]{unescapeHTML}); 
			} 
			interceptor = new DecodeInterceptor(escapeHTML, mappers); 
		} 
		return interceptor; 
	} 
 
	/** 
	 * mybatis 自定义拦截器 
	 * @return 
	 */ 
	@Bean 
	public EncodeInterceptor getEncodeInterceptor(@Value("${escapeHTML}") boolean escapeHTML){ 
		EncodeInterceptor interceptor = new EncodeInterceptor(escapeHTML); 
		return interceptor; 
	} 
} 

 

标签:Spring Boot
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号