草之戒_
posts - 2,comments - 14,trackbacks - 0
      当我们在做web应用的时候都会处理客户端提交到服务器的数据,如去除前后空格,一些非常字符,SQL注入类似的东西,在这里我主要说前后空格我是怎么来解决的,其它也都可以照此方法快速、方便、有效的解决,但是我一般对于非法字符,都是采用了标签来解决它的,并没有使用Filter转义掉(纯属个人解决办法)。
      去除前后空格看似非常简单的事,但是有许多人可能就是因为这一个小问题,折磨自己半天,客户端提交到所有的东西,都是以字符串形式提交的,我们不知道客户是怎么操作的,他可能把一个age属性对应的值,在输入时多加了一个空格,而服务器age对应的却是Integer类型,如果你使用servlet这事也好解决,但是如果你使用的是MVC框架,自动封装时就会得到一个类型转换异常,然而这个时候你是否有好的解决办法呢?
      这里我使用Filter来解决这一问题,这是最简单方便有效的解决方式,因为你不需要对每一个属性在封装前都去trim(),因为这是一件非常乏味的事情。大家都知道filter可以过滤我们想要它过滤的每一个请求,在这请求中有HttpServletRequest、HttpServletResponse。我们知道服务器取得客户端发送的参数都是通过HttpServletRequest来获取的,那我们可不可以在使用HttpServletRequest取值的时候就为每一个客户端提交的属性去除前后空格,或者其它的一些过滤操作。这肯定是可以的,那我们先来了解一下服务器是怎么取得客户端的值的。
1.getParameter(name),返回单个值。
2.getParameterValues(name),返回一个数组。
3.getParameterMap(),把客户端提交参数封装为一个Map返回。K:name,V:value。
      当我们使用servlet的时候一般都是使用前两种,struts1使用的第2种,struts2(xwork)则使用的第3种,那么我们只要在这三个方法调用的时候处理前后空格,那么返回到服务器的参数就又减少了一分出bug的机会,看下面的实现。
public class RequestParameterFilter implements Filter {

    
private static Log log = LogFactory.getLog(RequestParameterFilter.class);

    
private List<String> excludeNames;

    
public void destroy() {

    }

    
public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) 
throws IOException, ServletException {
        
//
        request = new HttpServletRequestWrapper2((HttpServletRequest) request);
        chain.doFilter(request, response);
    }

    
public void init(FilterConfig config) throws ServletException {
        String exclude 
= config.getInitParameter("exclude");
        
// not is null.
        if (exclude != null && exclude.length() > 0) {
            excludeNames 
= Arrays.asList(exclude.split(","));
            
if (log.isDebugEnabled()) {
                log.debug(
"initialize arguments.");
            }
        }
    }

    
/**
     * 该类继承之HttpServletRequestWrapper,并重写了对应取得客户端相当参数值的所有的方法。
     * <ul>
     * <li>getParameter</li>
     * <li>getParameterValues</li>
     * <li>getParameterMap</li>
     * </ul>
     * 
     * 
@version 1.0/2010-6-10 上午11:25:47
     * 
@author Aidan
     * 
@see HttpServletRequestWrapper
     
*/
    
private class HttpServletRequestWrapper2 extends HttpServletRequestWrapper {

        
private ParameterMap2 pm2;

        
public HttpServletRequestWrapper2(HttpServletRequest request) {
            
super(request);
        }

        
public String getParameter(String name) {
            
if (excludeNames != null && excludeNames.contains(name)) {
                
return super.getParameter(name);
            }
            
return trim(super.getParameter(name));
        }

        @SuppressWarnings(
"unchecked")
        
public Map getParameterMap() {
            
// xwork便使用此方法取值
            
// 该方法返回一个Map,Map映射了客户端请求对应的键值(K,V)。
            if (pm2 == null) {
                pm2 
= new ParameterMap2(super.getParameterMap());
            }
            
return pm2;
        }

        
public String[] getParameterValues(String name) {
            
// Struts1使用此方法取得所有的参数值
            if (excludeNames != null && excludeNames.contains(name)) {
                
return super.getParameterValues(name);
            }
            
return (String[]) trim(super.getParameterValues(name));
        }
    }

    
/**
     * 该此继承自HashMap。
     * 
     * 
@version 1.0/2010-6-10 上午11:30:13
     * 
@author Aidan
     * 
@see HashMap
     
*/
    @SuppressWarnings( { 
"unchecked""serial" })
    
private class ParameterMap2 extends HashMap {

        
private Set entrySet;

        
/**
         * 若要构造此类对象,则需要传入一个map参数,该map对应的客户端请求的参数(K,V)。
         * 
         * 
@param map
         *            映射客户端参数。
         
*/
        
public ParameterMap2(Map map) {
            
super(map);
        }

        
public Set entrySet() {
            
// xwork使用了此方法取值
            if (entrySet == null) {
                entrySet 
= new HashSet();
                Set temSet 
= super.entrySet();
                
for (Iterator iterator = temSet.iterator(); iterator.hasNext();) {
                    Map.Entry me 
= (Map.Entry) iterator.next();
                    Entry2 entry 
= new Entry2(me);
                    entrySet.add(entry);
                }
            }
            
return entrySet;
        }

        
// 若直接从map使用key取得
        public Object get(Object key) {
            Object value 
= super.get(key);
            
// 不过滤此对象
            if (excludeNames != null && excludeNames.contains(key)) {
                
return value;
            }
            
if (value != null) {
                
return trim(value);
            }
            
return null;
        }
    }

    @SuppressWarnings(
"unchecked")
    
private class Entry2<K, V> implements Map.Entry<K, V> {
        
private Map.Entry me;
        
private boolean isTrim = true;

        
public Entry2(Map.Entry me) {
            
if (me == null) {
                
throw new IllegalArgumentException(
                        
"Map.Entiry argument not null.");
            }
            
this.me = me;
            
// 不过滤此对象
            if (excludeNames != null && excludeNames.contains(me.getKey())) {
                isTrim 
= false;
            }
        }

        
public K getKey() {
            
return (K) me.getKey();
        }

        
public V getValue() {
            
if (isTrim) {
                
return (V) trim(me.getValue());
            }
            
return (V) me.getValue();
        }

        
public V setValue(V value) {
            
return (V) me.setValue(value);
        }

    }

    
/**
     * 去除一个Object类型对应的前后空格,因为客户端提交参数有两种,一种:String,另一种:String[],此方法会自动判断调用哪个方法。
     * 
     * 
@param value
     *            需要处理的参数。
     * 
@return 处理后的值。
     
*/
    
protected Object trim(Object value) {
        
if (value instanceof String[]) {
            
return trim((String[]) value);
        }
        
return trim(value.toString());
    }

    
/**
     * 去除某个字符串的前后空格。
     * 
     * 
@param value
     *            需要处理的参数。
     * 
@return 处理后的值。
     
*/
    
protected String trim(String value) {
        
if (value != null && value.length() > 0) {
            
return value.trim();
        }
        
return value;
    }

    
/**
     * 去除某个数组中所有的值的前后空格。
     * 
     * 
@param values
     *            需要处理的数组。
     * 
@return 处理后的值,当数组的length为1时,则返回一个String,反之返回一个数组。
     
*/
    
protected Object trim(String[] values) {
        
if (values != null && values.length > 0) {
            
int len = values.length;
            
for (int i = 0; i < len; i++) {
                values[i] 
= trim(values[i]);
            }
        }
        
if (values.length == 1) {
            
return values[0];
        }
        
return values;
    }

    
/**
     * 
     * 
@return 不处理的对象。
     
*/
    
public List<String> getExcludeNames() {
        
return excludeNames;
    }

}
      这个Filter实现原理非常简单,我会过滤所有的请求,HttpServletRequestWrapper2继承自HttpServletRequestWrapper,在构造函数中需要一个HttpServletRequest对象(这个request是web窗口创建的),然后我重载了上面所说的3个方法,在方法内部每次会去过滤当前值,这是利用了Java多态特性。在使用getParameterMap时较为麻烦,原理一样。
      当然我们有时候可能有些特殊情况不需要过滤前后空格或者其它一些规则,这里我们可以使用exclude属性来判断是否过滤此属性。
DEMO:
<form action="test!create.action" method="post">
            name:
            
<input name="name" value=" My name is haha.. " />
            
<br />
            
<!-- This is a String,isn't number. -->
            age:
            
<input name="age" value="   15  " />
            
<br />
            email:
            
<input name="email" value=" grasszring@gmail.com " />
            
<br />
            email2:
            
<input name="email" value=" grasszring@foxmail.com " />
            
<br />
            
<input type="submit" value=" submit " />
</form>
web.xml
<filter>
        
<filter-name>requestParameter</filter-name>
        
<filter-class>com.onlyeffort.commons.web.filter.RequestParameterFilter</filter-class>
        
<init-param>
            
<!-- 不需要过滤此参数 -->
            
<param-name>exclude</param-name>
            
<param-value>email</param-value>
        
</init-param>
    
</filter>
    
<filter-mapping>
        
<filter-name>requestParameter</filter-name>
        
<url-pattern>/*</url-pattern>
</filter-mapping>
action
@Action(params = { "actionName""test" })
@Result(location 
= "http://www.google.com", type = "redirect")
@SuppressWarnings(
"serial")
public class TestController extends ActionSupport {

    
private String name;
    
private Integer age;
    
private String[] email;
        
//.. get set method.

    @Override
    
public String create() throws CreateFailureException {
        System.out.println(name);
        System.out.println(age);
        
for (String mail : email) {
            System.out.println(mail);
        }        
        
return SUCCESS;
    }
}

      OK,如果大家有什么问题或有什么意见都尽管留言,感激不尽。


转载时请注明转载地址,onlyeffort.QQ:501276913
posted on 2010-06-10 14:45 Aidan 阅读(2190) 评论(7)  编辑  收藏

FeedBack:
# re: 快速、方便、有效的Filter
2010-06-11 09:29 | regale
不错!学习了  回复  更多评论
  
# re: 快速、方便、有效的Filter
2010-06-13 00:30 | zcl
一个简单的功能有必要搞得这么复杂吗?虽然你这个filter可以去掉空格,也可以通过配置选择不去掉哪些字段的空格,但你在action中还是要判断传过来的属性是否为空,也就是说也要判断形如:if (name != null)...,其实搞开发的已经习惯了这种风格的代码:if (name != null &&!name.trim().equals(""))的形式了,这种写法已经很通用了,可读性很好!  回复  更多评论
  
# re: 快速、方便、有效的Filter
2010-06-13 08:36 | Aidan
@zcl
可能你没有仔细查看我的例子,你仔细看age是一个Integer类型,但是client传的却是一个不能转换为Integer的String字符串,这时候你使用MVC框架该如何?我在里面也清楚的说明了。  回复  更多评论
  
# re: 快速、方便、有效的Filter
2010-07-15 17:28 | jj
如果只是去空格的话,filter大材小用了。与灵活性相比,我宁愿写一个util来封装requst取到的param。搞的这么复杂的确没有必要  回复  更多评论
  
# re: 快速、方便、有效的Filter
2010-07-15 22:39 | Aidan
@jj
那我请问你,假如我们使用mvc框架的时候,你的util类如何使用??  回复  更多评论
  
# re: 快速、方便、有效的Filter
2010-07-16 00:07 | jj
@Aidan
与使用mvc框架什么关系?就不能用util类了?java.util的工具类你在mvc框架下从来不用?  回复  更多评论
  
# re: 快速、方便、有效的Filter
2010-07-16 00:35 | jj
@Aidan
还有,这么简单的一个应用又何必小题大做争论来争论去呢?你的实现没有问题,只不过是看有没有必要,还有很多更简洁更方便,至少exclude的时候不用重启容器的方法。不同情况,不同考虑。总之,这原本就是一个最最基本的问题,又有n多最最基本的解决方法,每种方法各有利弊,多说无益,相信有很多人浏览过却没有留言就是这个原因  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
 
编程部落   qq群:37996359(上限500人,一起关注java、讨论技术,互相学习,共同进步)