過濾器Filter
Filter過濾器
1.過濾器是什麼?
過濾器: 可以把"不和諧"的東西給過濾掉/篩選掉/排除掉.
生活中的過濾器:帶有過濾功能的淨水器,濾紙,香菸的過濾嘴,測試,丈母孃.
程式中的過濾器:雙向過濾器在Java中最小的程式單元是類,程式中的過濾器就是一個特殊的類.
Servlet/Filter是Web的一個元件.
2.過濾器的作用
我們可以簡單理解為:過濾器處於客戶端和服務端之間
過濾器可以對所有的請求或響應做攔截操作
-
以常規的方式呼叫資源(Servlet/JSP);
-
利用修改過的請求資訊呼叫資源;
(可以將請求過來的資料修改後 再取呼叫資源)
-
呼叫資源之後,但在響應到客戶端之前,對響應做出修改;
(可以將返回的響應修改後,交給客戶端)
-
阻止當前資源呼叫,代之轉到其他資源.
3.過濾器的應用
首先說明一下開發中常用的思想
1. DRY原則
Don't Repeat Yourself 開發中拒絕程式碼重複(重複會帶來巨大的維護成本)
2. 責任分離原則
各自做各自最擅長的事情
過濾器在開發中的運用:
-
可以對請求中的字元做編碼.
有時候我們會給Servlet設定編碼格式
例如:request.setCharacterEncoding("UTF-8"); 但是要是很多Servlet都設定 造成了程式碼重複 所以我們可以在Filter中設定編碼格式
-
登陸驗證過濾器.
登陸驗證操作 同樣也是為了避免程式碼重複
將登陸驗證操作寫在Filter中 後面請求Servlet資源就不需要驗證了
-
敏感字(非法文字)過濾.
有時候請求來的資料中會帶有一些敏感詞彙
經過Filter時 ,會將這些敏感詞彙代替或者轉義
-
做MVC框架中的前端控制器.(處理所有請求共同的操作,再分發)
4.過濾器的開發和使用
因為Servlet和Filter都是Web的元件,這裡可以使用 我們可以進行對比記憶。
Servlet開發
1:自定義一個類(XxxServlet),實現於javax.servlet.Servlet介面.
2:實現Servlet介面中的方法(init(初始化方法),service(處理請求)).
3:告訴Tomcat來幫我們管理該Servlet程式(1:使用web.xml 做配置,2:WebServlet("/資源名")).
<servlet>
<servlet-name>Servlet的別名</servlet-name>
<servlet-class>自定義Servlet的全限定名</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet的別名</servlet-name>
<url-pattern>/資源名稱</utl-pattern>
</servlet-mapping>
注意:此時的url-pattern的文字內容是外界訪問是Servlet的資源名稱.
Filter開發
1:自定義一個類(XxxFilter),實現於javax.servlet.Filter介面.
2:實現Filter介面中的方法(init(初始化方法),doFilter(執行過濾操作)).
在啟動Tomcat的時候,就建立好物件,並呼叫init方法做初始化操作.
3:告訴Tomcat來幫我們管理該Filter程式(1:使用web.xml做配置,2:WebFilter("/資源名")).
<filter>
<filter-name>Filter的別名</filter-name>
<filter-class>自定義Filter的全限定名</filter-class>
</filter>
<filtert-mapping>
<filter-name>Filter的別名</filter-name>
<url-pattern>/資源名稱</utl-pattern>
</filter-mapping>
注意:此時的url-pattern的文字內容是Filter對哪一些資源做過濾操作.
如: /hello.jsp :說明當前Filter只會對/hello.jsp做攔截/過濾.
/employee :說明當前Filter只會對/employee資源(Servlet)做過濾.
/system/* :說明當前Filter只會對以/system/作為字首的資源路徑做攔截.
FilterChain(過濾器鏈)
FilterChain(過濾器鏈):多個過濾器按照一定的順序,排列起來.抽象的可以看為 多個過濾器組成一條鏈
程式中,存在多個過濾器的時候,過濾器的先後執行順序由誰來決定?
由在web.xml中:配置的
過濾器的對映細節
1:Filter中的url-pattern的文字內容是Filter對哪一些資源做過濾操作.
如: /hello.jsp :說明當前Filter只會對/hello.jsp做攔截/過濾.
/employee :說明當前Filter只會對/employee資源做過濾.
/system/* :說明當前Filter只會對以/system/作為字首的資源路徑做攔截.
/* :說明對所有的資源做過濾.
注:url-pattern 可以有多個 可對很多資源同時做攔截操作
2:Filter的dispatcher(表示對哪些動作做過濾).
攔截器預設為只對請求做攔截
5.請求編碼過濾器
CharacterEncodingFilter:
請求編碼過濾器就是在過濾器中設定請求的資料的編碼格式
這樣就不需要在後面的Servlet中設定這一行程式碼了
req.setCharacterEncoding("UTF-8");
但是我們一般在原始碼中不能明文寫自己設定的編碼 存在硬編碼問題
應該將編碼格式設定在初始化引數中,從初始化引數獲取
可以在web.xml中配置filter時 設定初始化引數 這樣利於維護 只需要該配置檔案即可
<!-- 配置初始化引數 自定義編碼 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
將encoding設定為成員變數 之後可以在請求編碼過濾器Filter的init方法中取出這個encoding
private String encoding;
@Override
public void init(FilterConfig config) throws ServletException {
// 編碼要從配置web.xml中獲取 不能用明文顯示
this.encoding = config.getInitParameter("encoding");
}
得到設定的編碼格式之後,我們在doFilter中開始設定
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 型別轉換 轉為http型別
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 應用中沒有設定編碼(req.getCharacterEncoding() == null 的情況)
// 並且我自己設定了編碼 則設定。 若為false,就使用預設編碼(ISO8859-1)
if (hasLength(encoding) && req.getCharacterEncoding() == null) {
req.setCharacterEncoding(encoding);
}
//放行
chain.doFilter(req, resp);
}
上述問題是第一種情況:應用中沒有設定編碼並且我自己設定了編碼 則設定
但是我們還要考慮一種情況,就是之前已經有人設定了編碼,我又定義了編碼,此時是否使用我定義的編碼
這裡要引出一個 是否強制編碼的問題 (是否使用我的編碼)
是否強制編碼問題
這裡我們還是可以在web.xml中filter中設定一個引數 表示是否強制編碼 預設為false
<!-- 設定是否強制編碼 -->
<init-param>
<param-name>force</param-name>
<param-value>false</param-value>
</init-param>
跟上述方法一樣 將其設定為成員變數 從init方法中獲取
// 是否強制編碼
private Boolean forceEncoding = false;
@Override
public void init(FilterConfig config) throws ServletException {
// 編碼要從配置web.xml中獲取 不能用明文顯示
this.encoding = config.getInitParameter("encoding");
forceEncoding = Boolean.valueOf(config.getInitParameter("force"));
}
考慮這種情況時,設定編碼的判斷就要再加一項 判斷是否要硬編碼 這樣就解決了編碼問題
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 型別轉換 轉為http型別
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 設定編碼
// 1.應用中沒有設定編碼(req.getCharacterEncoding() == null 的情況)並且我自己設定了編碼
// 2.應用中已經存在編碼但是要使用自定義的編碼 : 強制使用
if (hasLength(encoding) && req.getCharacterEncoding() == null || forceEncoding) {
req.setCharacterEncoding(encoding);
}
//放行
chain.doFilter(req, resp);
}
6.登入驗證過濾器
CheckLoginFilter
首先我們要明確 登入驗證過濾器 是要過濾那些需要驗證身份的頁面
而登入頁面和接收登入請求的Servlet是不需要被過濾的
所以,如果我們將CheckLoginFilter的url-pattern設定為 /* 則要將這兩項去除過濾
<filter>
<filter-name>CheckLoginFilter</filter-name>
<filter-class>com.yhnit._03_checklogin.CheckLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CheckLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
我們可以將不需要過濾的資源定義在一個數組中,但是這裡還是不嚴謹,不應該明文顯示,應該從初始化引數中取出 但是這個過濾器用的不多,更多的是攔截器,所以我這裡從簡了
// 定義不需要被過濾的資源
// 其實這裡不應該使用明文設定 應該從配置檔案web.xml中獲取初始化引數 後面要學攔截器
private String[] UncheckUris = {"/login.jsp","/login"};
doFilter方法 判斷當前過濾資源是否是 不需要過濾的資源,如果是,則放行。 不是,則要身份判斷。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 表示當前過濾的資源
String uri = req.getRequestURI();
System.out.println("當前被過濾的資源" + uri);
// 登陸頁面和登陸Servlet不需要過濾
if (!Arrays.asList(UncheckUris).contains(uri)) {
Object user = req.getSession().getAttribute("USER_IN_SESSION");
if (user == null) {
resp.sendRedirect("/login.jsp");
return;
}
}
// 已經登入 放行
chain.doFilter(req, resp);
}
7.敏感字過濾器
敏感字過濾器 主要是客戶端(瀏覽器)填寫表單資料等資訊時,存在敏感字,提交資料時,過濾器可以先將敏感字進行處理,比如轉義或者變為*符號,然後Servlet獲取的引數就是過濾器處理後的資料值
設計思想:
這裡最主要的是 重寫getParameter 這個方法 讓Servlet處理請求時,用這個重寫的方法,
因為重寫的方法中有過濾功能
定義一個包裝類MessageRequestWapper 裡面重寫getParameter 這個方法 方法裡有過濾功能
在doFilter方法中將 請求req 包裝為請求Servlet的請求
HttpServletRequest requestWapper = new MessageRequestWapper(req);
當請求Servlet時,Servlet中的req引數其實就是現在的包裝後的請求requestWapper
然後呼叫包裝類重寫的getParameter 方法 即可完成過濾功能。
過濾功能實現(將敏感字寫在stopwords集合中):