Filter過濾器API
Servlet過濾器API包含了3個(gè)接口,它們都在javax.servlet包中,分別是Filter接口、FilterChain接口和FilterConfig接口。
Filter接口(源碼)
public interface Filter { public void init(FilterConfig filterConfig) throws ServletException; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; public void destroy();}
所有的過濾器都必須實(shí)現(xiàn)Filter接口。該接口定義了init,doFilter0,destory()三個(gè)方法:
(1)init(FilterConfig filterConfig)
在web應(yīng)用程序啟動時(shí),web服務(wù)器將根據(jù) web.xml文件中的配置信息來創(chuàng)建每個(gè)注冊的Filter實(shí)例對象,并將其保存在服務(wù)器的內(nèi)存中。Web容器創(chuàng)建Filter對象實(shí)例后,將立即調(diào)用該Filter對象的init方法。Init方法在Filter生命周期中僅執(zhí)行一次,web容器在調(diào)用init方法時(shí),會傳遞一個(gè)包含F(xiàn)ilter的配置和運(yùn)行環(huán)境的FilterConfig對象(FilterConfig的用法和ServletConfig類似)。利用FilterConfig對象可以得到ServletContext對象,以及部署描述符中配置的過濾器的初始化參數(shù)。
(2)doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
doFilter()方法類似于Servlet接口的service()方法。當(dāng)客戶端請求目標(biāo)資源的時(shí)候,容器就會調(diào)用與這個(gè)目標(biāo)資源相關(guān)聯(lián)的過濾器的 doFilter()方法。其中參數(shù) request, response 為 web 容器或 Filter 鏈的上一個(gè) Filter 傳遞過來的請求和相應(yīng)對象;參數(shù) chain 為代表當(dāng)前 Filter 鏈的對象,在特定的操作完成后,可以在當(dāng)前 Filter 對象的 doFilter 方法內(nèi)部需要調(diào)用 FilterChain 對象的 chain.doFilter(request,response)方法才能把請求交付給 Filter 鏈中的下一個(gè) Filter 或者目標(biāo) Servlet 程序去處理,也可以直接向客戶端返回響應(yīng)信息,或者利用RequestDispatcher的forward()和include()方法,以及 HttpServletResponse的sendRedirect()方法將請求轉(zhuǎn)向到其他資源。這個(gè)方法的請求和響應(yīng)參數(shù)的類型是 ServletRequest和ServletResponse,也就是說,過濾器的使用并不依賴于具體的協(xié)議。
(3)public void destroy()
在Web容器卸載 Filter 對象之前被調(diào)用。該方法在Filter的生命周期中僅執(zhí)行一次。在這個(gè)方法中,可以釋放過濾器使用的資源。
FilterChain接口(源碼)
public interface FilterChain { public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;}
(1)doFilter(ServletRequest request,ServletResponse response)
此方法是由Servlet容器提供給開發(fā)者的,用于對資源請求過濾鏈的依次調(diào)用,通過FilterChain調(diào)用過濾鏈中的下一個(gè)過濾 器,如果是最后一個(gè)過濾器,則下一個(gè)就調(diào)用目標(biāo)資源。
FilterConfig接口(源碼) FilterConfig接口檢索過濾器名、初始化參數(shù)以及活動的Servlet上下文。
public interface FilterConfig { //返回web.xml部署文件中定義的該過濾器的名稱 public String getFilterName(); //返回調(diào)用者所處的servlet上下文 public ServletContext getServletContext(); //返回過濾器初始化參數(shù)值的字符串形式,當(dāng)參數(shù)不存在時(shí),返回nul1.name是初始化參數(shù)名 public String getInitParameter(String name); //以Enumeration形式返回過濾器所有初始化參數(shù)值,如果沒有初始化參數(shù),返回為空 public Enumeration getInitParameterNames();}
了解了Filter的基本概念和源碼,下面具體使用下Filter過濾器來實(shí)現(xiàn)登錄過濾。
需求:訪問A頁面(登錄后才能訪問的頁面)-->未登錄-->跳轉(zhuǎn)到登錄頁面-->登陸成功后,跳轉(zhuǎn)到A頁面
自定義HttpFilter
import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * HttpFilter */public abstract class HttpFilter implements Filter{ //保存filterConfig對象 PRivate FilterConfig filterConfig; /** * 直接返回filterConfig對象 * @return */ public FilterConfig getFilterConfig() { return filterConfig; } /** * 不建議子類直接覆蓋,若直接失敗,將可能導(dǎo)致filterConfig成員變量初始化失敗 */ @Override public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; init(); } /** * 供子類繼承的初始化方法,刻通過getFilterConfig()方法獲得filterConfig對象 */ private void init() {} /** * 原生的doFilter方法,在方法內(nèi)部把ServletRequest和ServletResponse轉(zhuǎn)化化為了HttpServletRequest和HttpServletResponse, * 并調(diào)用了doFilter(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain)方法 */ @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; doFilter(request, response, filterChain); } /** * 抽象方法,為http請求定制,必須實(shí)現(xiàn)的方法 * @param request * @param response * @param filterChain * @throws IOException * @throws ServletException */ public abstract void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException; @Override public void destroy() {}}
web.xml配置CommonFilter
<filter> <filter-name>commonFilter</filter-name> <filter-class>com.gcx.emall.Filter.CommonFilter</filter-class> </filter> <filter-mapping> <filter-name>commonFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
登錄過濾器CommonFilter
import java.io.IOException;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class CommonFilter extends HttpFilter { private final Logger log = LoggerFactory.getLogger(CommonFilter.class); @Override public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { log.info("==============攔截get請求================"); if ("GET".equalsIgnoreCase(request.getMethod())) { RequestUtil.saveRequest(request); } String requestUri = request.getRequestURI(); String contextPath = request.getContextPath(); String url = requestUri.substring(contextPath.length()); if ("/login".equals(url)) { filterChain.doFilter(request, response); return; } else { String username = (String) request.getsession().getAttribute("user"); if (username == null) { log.info("被攔截:跳轉(zhuǎn)到login頁面!"); request.getRequestDispatcher("/page/index1.jsp").forward(request, response); } else filterChain.doFilter(request, response); } }}
RequestUtil 保存、獲取request并加密請求頁面
public class RequestUtil { private static final Logger logger = LoggerFactory.getLogger(RequestUtil.class); private static final Base64 base64 = new Base64(true); public static final String LAST_PAGE = "lastPage";//未登錄時(shí)訪問的頁面 public static final String REDIRECT_HOME = "/";//未登錄時(shí)跳轉(zhuǎn)到首頁 public static final String LOGIN_HOME = "/index.jsp";//登錄成功后進(jìn)入的頁面 /** * 保存當(dāng)前請求 */ public static void saveRequest(HttpServletRequest request) { request.getSession().setAttribute(LAST_PAGE, RequestUtil.hashRequestPage(request)); logger.debug("被攔截的url的sessionID:{}", request.getSession().getId()); logger.debug("save request for {}", request.getRequestURI()); } /** * 加密請求頁面 * @param request * @return */ public static String hashRequestPage(HttpServletRequest request) { String reqUri = request.getRequestURI(); String query = request.getQueryString(); if (query != null) { reqUri += "?" + query; } String targetPage = null; try { targetPage = base64.encodeAsString(reqUri.getBytes("UTF-8")); } catch (UnsupportedEncodingException ex) { //this does not happen } return targetPage; } /** * 取出之前保存的請求 * @return */ public static String retrieveSavedRequest(HttpServletRequest request) { HttpSession session = request.getSession(); if (session == null) { return REDIRECT_HOME; } String HashedlastPage = (String) session.getAttribute(LAST_PAGE); if (HashedlastPage == null) { return LOGIN_HOME; } else { return retrieve(HashedlastPage); } } /** * 解密請求的頁面 * @param targetPage * @return */ public static String retrieve(String targetPage) { byte[] decode = base64.decode(targetPage); try { String requestUri = new String(decode, "UTF-8"); int i = requestUri.indexOf("/", 1); return requestUri.substring(i); } catch (UnsupportedEncodingException ex) { //this does not happen return null; } }}
LoginCOntroller
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String testHello( String test) {
log.info("執(zhí)行了Hello方法!");
return "loginSuccess";
}
@RequestMapping(value = "/login",method = RequestMethod.POST) public String login(HttpServletRequest request,String userName,String passWord){ log.info("執(zhí)行了login方法!"); password = DigestUtils.md5Hex(password); User user = userService.findUser(userName,password); if(user!=null){ request.getSession().setAttribute("userId", user.getId()); request.getSession().setAttribute("user", userName); return "redirect:" + RequestUtil.retrieveSavedRequest(request);//跳轉(zhuǎn)至訪問頁面 }else{ log.info("用戶不存在"); request.getSession().setAttribute("message", "用戶名不存在,請重新登錄"); return "index"; } }
最后需要幾個(gè)jsp頁面login.jsp,index.jsp(首頁面,任何人都能訪問的),loginSuccess.jsp,還需要在controller中加上一個(gè)測試testHello方法用于滿足之前說的需求。
注意事項(xiàng):我們過濾的是所有請求,但對于靜態(tài)資源CSS,js,image我們應(yīng)該不攔截,對其放行。我們可以在web.xml中進(jìn)行指定
<!-- 不攔截靜態(tài)文件 --> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/js/*</url-pattern> <url-pattern>/css/*</url-pattern> <url-pattern>/image/*</url-pattern> <url-pattern>/fonts/*</url-pattern> </servlet-mapping>
寫在后面:本來想把Filter和SpringMVC的interceptor攔截器一起寫總結(jié)了,但感覺篇幅有些長打算下篇在介紹。
新聞熱點(diǎn)
疑難解答
圖片精選