springboot项目敏感词屏蔽html页面modelAndView中值基于HandlerInterceptor实现已及外围接口Response中值屏蔽基于Filter
1 需求背景:
网站内容防止触发广告法,需要自动屏蔽些关键字;例如: "最", "第一" 等;可以通过后台配置及时生效
2 html页面屏蔽实现:
中小型公司官网等项目,日访问量不会很大,而且要考虑SEO问题,而且基本就1个程序员.项目选型大多数都不会使用前后端分离技术,而是选择前后端一体单应用,
例如:上古时期的springMVC+JSP 及后来的 springboot + html + 模板引擎(freemark,velocity,thymeleaf).
对于上述需求,要求屏蔽词语随时可以更改, 那么就不能在编写文章入库时候处理,只能在页面展示时候屏蔽,
在springboot返回页面传值时使用modelAndView赋值,那么怎么进行敏感词过滤呢?
其实很简单,HandlerInterceptor的 postHandle中会直接获取到modelAndView,对其ModelMap值遍历过滤就可以;屏蔽的关键字需要从数据库中读取
@Component public class BlackWordsInterceptor implements HandlerInterceptor { @Autowired private BlackWordsUtil blackWordsUtil; @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) { if(modelAndView != null){ ModelMap modelMap = modelAndView.getModelMap(); modelMap.forEach((k,v) -> { try { //跳过敏感词校验的key if(!CST.BLACK_WORDS_SKIP_KEY.contains(k)){ String s = BeanUtil.beanToJson(v); s = blackWordsUtil.doFilter(s); //过滤后的值覆盖旧值 modelMap.addAttribute(k, BeanUtil.jsonToBean(s,v.getClass())); } } catch (Exception e) { } }); } } }
@Component public class BlackWordsUtil { public final String REPLACE_WORDS = "*"; @Autowired private IBlackWordsService blackWordsService; public String doFilter(String str){ if(StringUtils.isBlank(str)){ return str; } List<BlackWords> blackWords = blackWordsService.findAll(); for (BlackWords blackWord : ListUtil.nvlList(blackWords)) { str = str.replaceAll(blackWord.getValue(),getStr(blackWord.getValue().length())); } return str; } private String getStr(int l){ String s = ""; for(int i=0 ; i < l;i++){ s += REPLACE_WORDS; } return s; } }
3 提供接口过滤
@WebFilter(urlPatterns = {"/pc/*","/app/*","/uni/*"}, filterName = "blackWordsFilter") @Order(1) // 需要在启动类加@ServletComponentScan注解 urlPatterns 必须是 /* 结尾 public class BlackWordsFilter implements Filter { @Autowired private BlackWordsUtil blackWordsUtil; @Override public void init(FilterConfig filterConfig) throws ServletException { Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ResponseWrapper wrapperResponse = new ResponseWrapper((HttpServletResponse) response);//转换成代理类 // 这里只拦截返回,直接让请求过去,如果在请求前有处理,可以在这里处理 chain.doFilter(request, wrapperResponse); byte[] content = wrapperResponse.getContent();//获取返回值 //判断是否有值 if (content != null && content.length > 0) { String str = new String(content, "UTF-8"); String outStr = blackWordsUtil.doFilter(str); //把返回值输出到客户端 byte[] outBytes = outStr.getBytes(); ServletOutputStream out = response.getOutputStream(); response.setContentLength(outBytes.length);//重新设置返回长度 out.write(outBytes); out.flush(); } } @Override public void destroy() { Filter.super.destroy(); } }
public class ResponseWrapper extends HttpServletResponseWrapper { private ByteArrayOutputStream buffer; private ServletOutputStream out; public ResponseWrapper(HttpServletResponse httpServletResponse) { super(httpServletResponse); buffer = new ByteArrayOutputStream(); out = new WrapperOutputStream(buffer); } @Override public ServletOutputStream getOutputStream() throws IOException { return out; } @Override public void flushBuffer() throws IOException { if (out != null) { out.flush(); } } public byte[] getContent() throws IOException { flushBuffer(); return buffer.toByteArray(); } class WrapperOutputStream extends ServletOutputStream { private ByteArrayOutputStream bos; public WrapperOutputStream(ByteArrayOutputStream bos) { this.bos = bos; } @Override public void write(int b) throws IOException { bos.write(b); } @Override public boolean isReady() { // TODO Auto-generated method stub return false; } @Override public void setWriteListener(WriteListener arg0) { // TODO Auto-generated method stub } } }
4 测试效果
1页面屏蔽
2 接口屏蔽