當我們在做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 閱讀(2224)
評論(7) 編輯 收藏