servlet過(guò)濾器是小型的web組件,它能夠處理傳入的請(qǐng)求和傳出的響應(yīng)。Filter
不是一個(gè)servlet,它不能產(chǎn)生一個(gè)response,它能夠在一個(gè)request到達(dá)servlet之前預(yù)處理request,也可以在離開
servlet時(shí)處理response。它具有高度的透明性,無(wú)需更改應(yīng)用程序代碼,就可以根據(jù)需要添加、修改或從應(yīng)用程序中將它刪除。
一個(gè)filter 包括: 1. 在servlet被調(diào)用之前截獲; 2. 在servlet被調(diào)用之前檢查servlet request; 3. 根據(jù)需要修改request頭和request數(shù)據(jù); 4. 根據(jù)需要修改response頭和response數(shù)據(jù); 5. 在servlet被調(diào)用之后截獲. 你
能夠配置一個(gè)filter 到一個(gè)或多個(gè)servlet;單個(gè)servlet或servlet組能夠被多個(gè)filter
使用。幾個(gè)實(shí)用的filter包括:用戶辨認(rèn)filter,日志filter,審核filter,加密filter,符號(hào)filter,能改變xml內(nèi)容
的XSLT filter等。
一個(gè)客戶化的過(guò)濾器要實(shí)現(xiàn)Filter接口的三個(gè)方法:init()、destroy()和doFilter()。 1. init():在容器實(shí)例化過(guò)濾器時(shí)調(diào)用,該方法接受一個(gè)FilterConfig類型的對(duì)象做為輸入。 2. destroy():執(zhí)行一些清理操作。 3. doFilter():類似servlet的doPost()、doGet()方法,執(zhí)行具體的過(guò)濾任務(wù)。 下面給出一個(gè)可執(zhí)行程序范例,它包含main函數(shù),也可以部署到web應(yīng)用中。
package utils;

import java.io.IOException;
import java.util.*; import javax.servlet.*; import javax.servlet.http.*;
import bean.User;
public class UrlFilter implements Filter {

@SuppressWarnings("unused")
private FilterConfig filterConfig;
private FilterChain chain;
private HttpServletRequest request;
private HttpServletResponse response;
public void destroy() {
this.filterConfig = null;
}

public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}

public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
this.chain = chain;
this.request = (HttpServletRequest) servletRequest;
this.response = ((HttpServletResponse) servletResponse);
String url = request.getServletPath();
if (url == null)
url = "";
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
if (noFileUrl(url, request)) { //不需要判斷權(quán)限的請(qǐng)求如登錄頁(yè)面,則跳過(guò)
chain.doFilter(request, response);
} else if (user == null) {
String action = request.getParameter("action");
if (!"login".equals(action)) {
response.sendRedirect("/zmdsl/admin/index.jsp");//返回登錄頁(yè)面
}
} else {
verifyUrl(url, user);//判斷當(dāng)前user是否擁有訪問(wèn)此url的權(quán)限
}
}
/**
*
* @param url 當(dāng)前請(qǐng)求的url
* @param user 當(dāng)前登錄用戶
* @throws IOException
* @throws ServletException
*/
private void verifyUrl(String url, User user)throws IOException, ServletException {
// 獲取user擁有的所有資源串
Set<String> royurl = new HashSet<String>();
royurl.add("/newsAction.do?action=adminQueryPage&typeId=3");//為用戶分配訪問(wèn)此地址的權(quán)限。在真正項(xiàng)目中,此Set集合可能要
//通過(guò)查詢數(shù)據(jù)庫(kù)user、role、menu等表,來(lái)獲取當(dāng)前用戶所擁有的全部可訪問(wèn)資源
if (royurl != null && royurl.size() > 0
&& pass(royurl, url, request.getParameterMap())){
chain.doFilter(request, response);
} else {
response.setContentType("text/html;charset=GBK");
response
.getWriter()
.println(
"<div style='margin: 100 auto;text-align: center;"
+ "font: bold 18px 宋體;color: #0066CC;vertical-align: middle'> Sorry,您沒(méi)有權(quán)限訪問(wèn)該資源!</div>");
}
}

/**
* 是否需要判斷權(quán)限,如客戶端瀏覽、登錄頁(yè)面則不需要判斷權(quán)限
*/

protected boolean noFileUrl(String url, HttpServletRequest request) {
if (url.indexOf("/index.jsp") >= 0 || url.indexOf("login") > 0) {
return true;
}
return false;
}

/**
* 判斷該用戶是否有權(quán)請(qǐng)求該url
* @param royurl user擁有的授權(quán)的的url串集合
* @param url 當(dāng)前請(qǐng)求的url
* @param reqmap 當(dāng)前request的參數(shù)
* @return 是否通過(guò)該url
*/

protected boolean pass(Set royurl, String url, Map reqmap) {
boolean match = true;
for (Iterator iter = royurl.iterator(); iter.hasNext();) {
// 獲取資源
match = true;
String res_string = (String) iter.next();
if (res_string.indexOf("*") > 0) {
res_string = res_string.substring(0, res_string.indexOf("*"));
if (url.substring(0, res_string.length()).equalsIgnoreCase(res_string)) {
return true; //增加通配符比較
}
}
// 分割url與參數(shù)
String[] spw = res_string.split("\\?"); //用"\\?" 轉(zhuǎn)義后即可得到正確的結(jié)
if (url.indexOf(spw[0]) < 0) {//為了方便,沒(méi)有寫成spw[0].equals(url)
match = false;
}

if (match && spw.length > 1) {
String[] spa = spw[1].split("\\&"); // 分拆各參數(shù)
for (int j = 0; j < spa.length; j++) {
String[] spe = spa[j].split("="); // 分拆鍵與值
String key = spe[0];
String value = "";
if (spe.length > 1) {
value = spe[1].trim();
}
// 輪詢
String[] values = (String[]) reqmap.get(key);
if (values != null) {
for (int k = 0; k < values.length; k++) {
if (value.equalsIgnoreCase(values[k])) {
match = true;
break;
}
match = false;
}
if (!match) {
break;
}
}
}
}
if (match) {
break;
}
}
return match;
}

@SuppressWarnings("unchecked")
public static void main(String[] args) {
UrlFilter filter = new UrlFilter();
Set royurl = new HashSet();//可訪問(wèn)的URL集合
royurl.add("/newsAction.do?typeId=1");
royurl.add("/newsAction.do?typeId=2");

//typeId為1時(shí)可以訪問(wèn),true
String url_1 = "/newsAction.do";
Map reqmap_1 = new HashMap();
reqmap_1.put("typeId", new String[]{"1"});
System.out.println("match false:" + filter.pass(royurl, url_1, reqmap_1));
//typeId為3時(shí)不可以訪問(wèn),false
String url_2 = "/newsAction.do";
Map reqmap_2 = new HashMap();
reqmap_2.put("typeId", new String[]{"3"});
System.out.println("match false:" + filter.pass(royurl, url_2, reqmap_2));
//url就不同,不可訪問(wèn),false
String url_3 = "/imageAction.do";
Map reqmap_3 = new HashMap();
reqmap_3.put("typeId", new String[]{"3"});
System.out.println("match false:" + filter.pass(royurl, url_3, reqmap_3));
}
}

配置 Servlet過(guò)濾器: 非常簡(jiǎn)單,只需要在web.xml中加入類似下面的代碼就可將過(guò)濾器部署到應(yīng)用程序。
<!--================權(quán)限 設(shè)置================-->
<filter>
<filter-name>Authentication</filter-name>
<filter-class>utils.UrlFilter</filter-class>
<init-param>
<param-name>onError</param-name>
<param-value>/index.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Authentication</filter-name>
<!-- 只過(guò)濾 .jsp 結(jié)尾的url, 其余的如 .do, .html, .jpg, .css 等不作過(guò)濾-->
<url-pattern>*.do</url-pattern>
</filter-mapping>
其中<filter-Class>是過(guò)濾器的路徑;<filter-mapping>定義了該過(guò)濾器只過(guò)濾以.do結(jié)尾的URL,當(dāng)然你也可以稍做修改讓它過(guò)濾.jsp文件。
|