最近项目当中需要用到关键字过滤,配置一个过滤器,只对POST请求进行过滤。废话不说,上码。
1 package cn.com.phone.servlet.filter; 2 3 import java.io.IOException; 4 import java.io.UnsupportedEncodingException; 5 import java.lang.reflect.InvocationTargetException; 6 import java.lang.reflect.Method; 7 import java.net.URLDecoder; 8 import java.util.Enumeration; 9 import java.util.Map; 10 11 import javax.servlet.Filter; 12 import javax.servlet.FilterChain; 13 import javax.servlet.FilterConfig; 14 import javax.servlet.ServletException; 15 import javax.servlet.ServletRequest; 16 import javax.servlet.ServletResponse; 17 import javax.servlet.http.HttpServletRequest; 18 import javax.servlet.http.HttpServletResponse; 19 20 import cn.com.phone.utils.consts.Consts; 21 import cn.com.phone.utils.log.PrintLog; 22 import cn.com.phone.utils.utils.KeywordFilterService; 23 24 /** 25 * 关键字过滤 26 * @author nyp 27 */ 28 public class KeywordFilter implements Filter { 29 30 @Override 31 public void destroy() { 32 PrintLog.printInfoLog(getClass(), 33 Consts.debugdefine.serlvetfilterdestroy); 34 } 35 36 @SuppressWarnings("unchecked") 37 @Override 38 public void doFilter(ServletRequest request, ServletResponse response, 39 FilterChain filterchain) throws IOException,UnsupportedEncodingException,ServletException{ 40 HttpServletRequest req = (HttpServletRequest) request; 41 HttpServletResponse res = (HttpServletResponse) response; 42 // http协议 43 PrintLog.printInfoLog(getClass(), req.getProtocol()); 44 // 请求方式 45 PrintLog.printInfoLog(getClass(), req.getMethod()); 46 if (req.getMethod().equals("POST")) { 47 req.setCharacterEncoding("utf-8"); 48 Enumeration e = req.getParameterNames();//得到所有的请求参数名称,以枚举组装 49 Map map = req.getParameterMap(); 50 Method method = null; 51 52 try { 53 method = map.getClass().getMethod("setLocked", 54 new Class[] { boolean.class }); 55 method.invoke(map, new Object[] { new Boolean(false) }); 56 } catch (Exception e1) { 57 e1.printStackTrace(); 58 } 59 60 while (e.hasMoreElements()) { 61 String par = (String) e.nextElement();//得到各个参数名,进而得到参数值 62 String value = req.getParameter(par); 63 URLDecoder.decode(value, "UTF-8"); 64 String tempvalue = new String(value.getBytes("iso-8859-1"), "UTF-8"); 65 //调用关键字过滤方法 66 String newvalue = KeywordFilterService.getFilterString( 67 tempvalue, "*"); 68 map.put(par, newvalue); 69 } 70 } 71 filterchain.doFilter(req, res); 72 } 73 74 @Override 75 public void init(FilterConfig arg0) throws ServletException { 76 PrintLog 77 .printInfoLog(getClass(), Consts.debugdefine.serlvetfilterinits); 78 } 79 80 }
下面是关键字过滤处理的代码:
package cn.com.phone.utils.utils; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import cn.com.phone.jdbc.DBoperation; public class KeywordFilterService { private static final char endTag = (char) (1); // 关键词结束符 @SuppressWarnings("unchecked") private static Map<Character, HashMap> filterMap = new HashMap<Character, HashMap>( 1024); private static java.util.List<HashMap> key_list_map = new ArrayList<HashMap>(); private static java.util.List key_list = new ArrayList(); static{ String sql = "SELECT * FROM KEYWORD "; key_list_map = DBoperation.queryForObjectList(DBoperation.getConn(), null, sql); init(); System.out.println("key_list_map = "+key_list_map); } @SuppressWarnings("unchecked") public static void init() { for(Map map:key_list_map){ key_list.add(map.get("name")); } for (Object filterWord : key_list) { char[] charArray = filterWord.toString().trim().toCharArray(); int len = charArray.length; if (len > 0) { Map<Character, HashMap> subMap = filterMap; for (int i = 0; i < len - 1; i++) { Map<Character, HashMap> obj = subMap.get(charArray[i]); if (obj == null) { // 新索引,增加HashMap int size = (int) Math.max(2, 16 / Math.pow(2, i)); HashMap<Character, HashMap> subMapTmp = new HashMap<Character, HashMap>( size); subMap.put(charArray[i], subMapTmp); subMap = subMapTmp; } else { // 索引已经存在 subMap = obj; } } // 处理最后一个字符 Map<Character, HashMap> obj = subMap.get(charArray[len - 1]); if (obj == null) { // 新索引,增加HashMap,并设置结束符 int size = (int) Math.max(2, 16 / Math.pow(2, len - 1)); HashMap<Character, HashMap> subMapTmp = new HashMap<Character, HashMap>( size); subMapTmp.put(endTag, null); subMap.put(charArray[len - 1], subMapTmp); } else { // 索引已经存在,设置结束符 obj.put(endTag, null); } } } } // 返回是否包含需要过滤的词,匹配到最短的关键词就返回结果 @SuppressWarnings("unchecked") public static boolean isHasFilterWord(String info) { if (info == null || info.length() == 0) { return false; } char[] charArray = info.toCharArray(); int len = charArray.length; for (int i = 0; i < len; i++) { int index = i; Map<Character, HashMap> sub = filterMap.get(charArray[index]); while (sub != null) { if (sub.containsKey(endTag)) { // 匹配结束 return true; } else { index++; if (index >= len) { // 字符串结束 return false; } sub = sub.get(charArray[index]); } } } return false; } // 将字符串中包含的关键词过滤并替换为指定字符串,然后退回替换后的字符串 // 尽量匹配最长的关键词再替换 @SuppressWarnings("unchecked") public static String getFilterString(String info, String replaceTag) { if (info == null || info.length() == 0 || replaceTag == null || replaceTag.length() == 0) { return info; } char[] charArray = info.toCharArray(); int len = charArray.length; String newInfo = ""; int i = 0; while (i < len) { int end = -1; int index; Map<Character, HashMap> sub = filterMap; for (index = i; index < len; index++) { sub = sub.get(charArray[index]); if (sub == null) { // 匹配失败,将已匹配的最长字符进行替换 if (end == -1) { // 没匹配到任何关键词 newInfo += charArray[i]; i++; break; } else { // 将最长匹配字符串替换为特定字符 for (int j = i; j <= end; j++) { newInfo += replaceTag; } i = end + 1; break; } } else { if (sub.containsKey(endTag)) { // 匹配 end = index; } } } if (index >= len) { // 字符串结束 if (end == -1) { // 没匹配到任何关键词 newInfo += charArray[i]; i++; } else { // 将最长匹配字符串替换为特定字符 for (int j = i; j <= end; j++) { newInfo += replaceTag; } i = end + 1; } } } return newInfo; } }
实际上整过关键字过滤的思路就是,对请求参数值一个个取出来进行筛选和处理,最终得到过滤后的值。有个问题是,取出请求参数值进行处理后,怎样保证新值能替换掉旧值班呢?我甚至想过,通过现有的请求得到请求头请求路径和参数结合过滤过后的参数值重新发起一个请求。很可笑对吧。事实上,Httprequest提供了一个getParameterMap()的方法,通过它可以得到以参数名为KEY,参数值为VALUE的一个Map对象,然后可以直接通过put设置新值。但是这还没完,如果直接这样会抛异常,如下:
是的,ParameterMap被锁定了,事实上,通过反编译得到源码可以发现,ParameterMap的庐山真面为org.apache.catalina.util.ParameterMap,它并不是我们通常情况下所使用的HashMap对象,而是继承自它,而且在通过put()对它设值的时候,会首先判断它是否被锁定,而在默认情况下,它是被锁定的,这也好理解,一个网络请求如果可以随意修改请求内容,数据传输的风险必然极大。源码摘编如下:
public Object put(Object key, Object value) { if(locked)//首先会去判断当前对象是否被锁定,默认锁定 throw new IllegalStateException(sm.getString("parameterMap.locked")); else return super.put(key, value); } public void setLocked(boolean locked) { this.locked = locked; }
我们不能修改源码,但既然有一个setLocked方法,通过反射去解决这个问题比较容易了。如下:
Map map = req.getParameterMap(); try { Method method = map.getClass().getMethod("setLocked", new Class[] { boolean.class }); method.invoke(map, new Object[] { new Boolean(false) });//通过反射设定当前ParameterMap对象为不锁定状态
} catch (Exception e1) { e1.printStackTrace(); } map.put(par, newvalue);//newvalue为处理后的值
--------------------------------------------------------------------------------------------------------------------------------------------------------------
原谅我这一生不羁放纵爱撸码,爱沉默