Filter過濾器原理和登陸實現

Filter過濾器API
     Servlet過濾器API包含了3個接口,它們都在javax.servlet包中,分別是Filter接口、FilterChain接口和FilterConfig接口。
Filter接口(源碼)css

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();
}

全部的過濾器都必須實現Filter接口。該接口定義了init,doFilter0,destory()三個方法:
  (1)init(FilterConfig filterConfig)
      在web應用程序啓動時,web服務器將根據 web.xml文件中的配置信息來建立每一個註冊的Filter實例對象,並將其保存在服務器的內存中。Web容器建立Filter對象實例後,將當即調用該Filter對象的init方法。Init方法在Filter生命週期中僅執行一次,web容器在調用init方法時,會傳遞一個包含Filter的配置和運行環境的FilterConfig對象(FilterConfig的用法和ServletConfig相似)。利用FilterConfig對象能夠獲得ServletContext對象,以及部署描述符中配置的過濾器的初始化參數。
  (2)
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
java

      doFilter()方法相似於Servlet接口的service()方法。當客戶端請求目標資源的時候,容器就會調用與這個目標資源相關聯的過濾器的 doFilter()方法。其中參數 request, response 爲 web 容器或 Filter 鏈的上一個 Filter 傳遞過來的請求和相應對象;參數 chain 爲表明當前 Filter 鏈的對象,在特定的操做完成後,能夠在當前 Filter 對象的 doFilter 方法內部須要調用 FilterChain 對象的 chain.doFilter(request,response)方法才能把請求交付給 Filter 鏈中的下一個 Filter 或者目標 Servlet 程序去處理,也能夠直接向客戶端返回響應信息,或者利用RequestDispatcher的forward()和include()方法,以及 HttpServletResponse的sendRedirect()方法將請求轉向到其餘資源。這個方法的請求和響應參數的類型是 ServletRequest和ServletResponse,也就是說,過濾器的使用並不依賴於具體的協議。
 (3)public void destroy()
       在Web容器卸載 Filter 對象以前被調用。該方法在Filter的生命週期中僅執行一次。在這個方法中,能夠釋放過濾器使用的資源。
FilterChain接口(源碼)
web

public interface FilterChain {

    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}

(1)doFilter(ServletRequest request,ServletResponse response)
      此方法是由Servlet容器提供給開發者的,用於對資源請求過濾鏈的依次調用,經過FilterChain調用過濾鏈中的下一個過濾  器,若是是最後一個過濾器,則下一個就調用目標資源。
FilterConfig接口(源碼)  FilterConfig接口檢索過濾器名、初始化參數以及活動的Servlet上下文。
服務器

public interface FilterConfig {
  //返回web.xml部署文件中定義的該過濾器的名稱 public String getFilterName();
  //返回調用者所處的servlet上下文 public ServletContext getServletContext();
  //返回過濾器初始化參數值的字符串形式,當參數不存在時,返回nul1.name是初始化參數名 public String getInitParameter(String name);
  //以Enumeration形式返回過濾器全部初始化參數值,若是沒有初始化參數,返回爲空 public Enumeration getInitParameterNames();
}

 

瞭解了Filter的基本概念和源碼,下面具體使用下Filter過濾器來實現登陸過濾。
session

需求:訪問A頁面(登陸後才能訪問的頁面)-->未登陸-->跳轉到登陸頁面-->登錄成功後,跳轉到A頁面app

自定義HttpFilterjsp

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;
    }
    
    /**
     * 不建議子類直接覆蓋,若直接失敗,將可能致使filterConfig成員變量初始化失敗
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {        
        this.filterConfig = filterConfig;    
        init();
    }

    /**
     * 供子類繼承的初始化方法,刻經過getFilterConfig()方法得到filterConfig對象
     */
    private void init() {}

    /**
     * 原生的doFilter方法,在方法內部把ServletRequest和ServletResponse轉化化爲了HttpServletRequest和HttpServletResponse,
     * 並調用了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請求定製,必須實現的方法
     * @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() {}

}
View Code

web.xml配置CommonFilteride

    <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("被攔截:跳轉到login頁面!");
                request.getRequestDispatcher("/page/index1.jsp").forward(request, response);
            } else
                filterChain.doFilter(request, response);
        }
    }
}

RequestUtil 保存、獲取request並加密請求頁面this

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";//未登陸時訪問的頁面
    public  static  final  String  REDIRECT_HOME = "/";//未登陸時跳轉到首頁
    public  static  final  String  LOGIN_HOME = "/index.jsp";//登陸成功後進入的頁面
       
    
    /**
     * 保存當前請求
     */
    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;
        }
    }
}
View Code

LoginCOntroller

        @RequestMapping(value = "/hello",method = RequestMethod.GET)
     public String testHello( String test) {
         log.info("執行了Hello方法!");
         return "loginSuccess";
     }
@RequestMapping(value = "/login",method = RequestMethod.POST) public String login(HttpServletRequest request,String userName,String password){ log.info("執行了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);//跳轉至訪問頁面 }else{ log.info("用戶不存在"); request.getSession().setAttribute("message", "用戶名不存在,請從新登陸"); return "index"; } }

最後須要幾個jsp頁面login.jsp,index.jsp(首頁面,任何人都能訪問的),loginSuccess.jsp,還須要在controller中加上一個測試testHello方法用於知足以前說的需求。

注意事項:咱們過濾的是全部請求,但對於靜態資源css,js,image咱們應該不攔截,對其放行。咱們能夠在web.xml中進行指定

    <!-- 不攔截靜態文件 -->
    <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攔截器一塊兒寫總結了,但感受篇幅有些長打算下篇在介紹。

相關文章
相關標籤/搜索