Javaweb:Servlet過濾器以及常見應用展示
Filter簡介:
Filter也稱之為過濾器,它是Servlet技術中最實用的技術,WEB開發人員通過Filter技術,對web伺服器管理的所有web資源:例如Jsp, Servlet, 靜態圖片檔案或靜態 html 檔案等進行攔截,從而實現一些特殊的功能。
l特點:
•過濾器不是目標資源,是在訪問目標資源的前後執行的。
•過濾器的攔截是雙向的
•可以有多個過濾器。
•過濾器攔截是一堆目標資源。
Servlet API中提供了一個Filter介面,開發web應用時,如果編寫的Java類實現了這個介面,則把這個java類稱之為過濾器Filter。通過Filter
過濾器的工作原理
通過使用過濾器,可以攔截客戶端的請求和響應,檢視、提取或者以某種方式操作正在客戶端和伺服器之間進行交換的資料。
通過使用過濾器,可以對Web元件的前期處理和後期處理進行控制。
過濾器可以有多個,以構成一個過濾器鏈。Servlet容器會根據過濾器的配置情況來決定過濾器的呼叫次序。
過濾器Filter的實現和部署:
(1)實現一個過濾器Filter
定義的過濾器類必須要實現介面javax.servlet.Filter,並且實現該介面中定義的3個方法:
vod
void destroy():用於銷燬過濾器。
void doFilter(…):用於執行過濾操作。
(2)部署一個過濾器Filter
在web.xml配置檔案中部署Filter
用<filter>元素定義過濾器,在 web.xml 檔案中使用<filter>和<filter-mapping>元素對編寫的filter類進行註冊,並設定它所能攔截的資源,<filter>元素有兩個必要子元素:
<filter-name>用來設定過濾器的名字
<filter-class >
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>filter.TestFilter</filter-class>
</filter>
用<filter-mapping>配置過濾器的對映資訊,有兩個必要的子元素:
<filter-name>用來設定過濾器的名字
<url-pattern>用來設定被過濾的元件
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/*.jsp</url-pattern>
</filter-mapping>
例項:
過濾器類:
package cn.itcast.filter;
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;
/**
* 實現Filter介面,重寫方法
* 在web.xml進行配置
* @author Administrator
*/
public class FilterDemo1 implements Filter{
/**
* 初始化
*/
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* 每次請攔截的方式都執行(通過配置來決定)
* 由伺服器呼叫doFilter() -- 進去到過濾器
* FilterChain伺服器建立,把傳入進來(過濾器的資訊 )
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("站住,打劫!!");
// 放行
// 放行 -- 執行下一個過濾器 -- 沒有訪問目標資源
chain.doFilter(request, response);
// 去訪問Servlet
// System.out.println("小夥,再來一次吧!!");
}
/**
* 銷燬
*/
public void destroy() {
}
}
web.xml配置:
<!-- 配置過濾器 -->
<!-- 配置過濾器的資訊 -->
<filter>
<!-- 配置名字 -->
<filter-name>FilterDemo1</filter-name>
<!-- 包名+類名 -->
<filter-class>cn.itcast.filter.FilterDemo1</filter-class>
</filter>
<!-- 配置過濾器的對映 -->
<!-- 配置訪問方式 -->
<!--
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Filter鏈 --- FilterChain
•在一個web應用中,可以開發編寫多個Filter,這些Filter組合起來稱之為一個Filter鏈。
•web伺服器根據Filter在web.xml檔案中的註冊順序<mapping>,決定先呼叫哪個Filter,當第一個Filter的doFilter方法被呼叫時,web伺服器會建立一個代表Filter鏈的FilterChain物件傳遞給該方法。在doFilter方法中,開發人員如果呼叫了FilterChain物件的doFilter方法,則web伺服器會檢查FilterChain物件中是否還有filter,如果有,則呼叫第2個filter,如果沒有,則呼叫目標資源。
•Filter鏈實驗(檢視filterChain API文件)
Filter是如何實現攔截的?
Filter介面中有一個doFilter方法,當開發人員編寫好Filter,並配置對哪個web資源(攔截url)進行攔截後,WEB伺服器每次在呼叫web資源之前,都會先呼叫一下filter的doFilter方法,因此,在該方法內編寫程式碼可達到如下目的:
•呼叫目標資源之前,讓一段程式碼執行
•是否呼叫目標資源(即是否讓使用者訪問web資源)。
•web伺服器在呼叫doFilter方法時,會傳遞一個filterChain物件進來,filterChain物件是filter介面中最重要的一個物件,它也提供了一個doFilter方法,開發人員可以根據需求決定是否呼叫此方法,呼叫該方法,則web伺服器就會呼叫web資源的service方法,即web資源就會被訪問,否則web資源不會被訪問。
•呼叫目標資源之後,讓一段程式碼執行
Filter的生命週期:
init(FilterConfig filterConfig)throws ServletException:
•和我們編寫的Servlet程式一樣,Filter的建立和銷燬由WEB伺服器負責。 web 應用程式啟動時,web 伺服器將建立Filter 的例項物件,並呼叫其init方法進行初始化(注:filter物件只會建立一次,init方法也只會執行一次。示例 )
•開發人員通過init方法的引數,可獲得代表當前filter配置資訊的FilterConfig物件。(filterConfig物件見下頁PPT)
doFilter(ServletRequest,ServletResponse,FilterChain)
•每次filter進行攔截都會執行
•在實際開發中方法中引數request和response通常轉換為HttpServletRequest和HttpServletResponse型別進行操作
destroy():
•在Web容器解除安裝 Filter 物件之前被呼叫。
package cn.itcast.filter;
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;
/**
* 過濾器的生命週期
* @author Administrator
*
*/
public class FilterDemo2 implements Filter{
/**
* 初始化的操作
* 過濾器什麼建立呢? 伺服器啟動的時候建立過濾器的例項。
* 呼叫幾次? 呼叫一次
*/
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init...");
}
/**
* 什麼時候執行
* 每次請求的時候,過濾器配置滿足過濾的條件,有一次請求就會執行一次。
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter....");
chain.doFilter(request, response);
}
/**
* 過濾器什麼時候銷燬 伺服器關閉,移除專案
* 呼叫幾次 一次。
*/
public void destroy() {
System.out.println("destroy....");
}
}
FilterConfig介面:
使用者在配置filter時,可以使用<init-param>為filter配置一些初始化引數,當web容器例項化Filter物件,呼叫其init方法時,會把封裝了filter初始化引數的filterConfig物件傳遞進來。因此開發人員在編寫filter時,通過filterConfig物件的方法,就可獲得:
•String getFilterName():得到filter的名稱。
•String getInitParameter(String name): 返回在部署描述中指定名稱的初始化引數的值。如果不存在返回null.
•Enumeration getInitParameterNames():返回過濾器的所有初始化引數的名字的列舉集合。
•public ServletContext getServletContext():返回Servlet上下文物件的引用。
Filter的配置之<filter>(和上面有雷同)
<filter>
<filter-name>testFitler</filter-name>
<filter-class>org.test.TestFiter</filter-class>
<init-param>
<param-name>word_file</param-name>
<param-value>/WEB-INF/word.txt</param-value>
</init-param>
</filter>
<filter-name>用於為過濾器指定一個名字,該元素的內容不能為空。
<filter-class>元素用於指定過濾器的完整的限定類名。
<init-param>元素用於為過濾器指定初始化引數,它的子元素<param-name>指定引數的名字,<param-value>指定引數的值。在過濾器中,可以使用FilterConfig介面物件來訪問初始化引數。
Filter的配置之<filter-mapping>:
<filter-mapping>元素用於設定一個 Filter 所負責攔截的資源。一個Filter攔截的資源可通過兩種方式來指定:Servlet 名稱和資源訪問的請求路徑
•<filter-name>子元素用於設定filter的註冊名稱。該值必須是在<filter>元素中宣告過的過濾器的名字
•<url-pattern>設定 filter 所攔截的請求路徑(過濾器關聯的URL樣式)
•<servlet-name>指定過濾器所攔截的Servlet名稱。
•<dispatcher>指定過濾器所攔截的資源被 Servlet 容器呼叫的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,預設REQUEST。使用者可以設定多個<dispatcher> 子元素用來指定 Filter 對資源的多種呼叫方式進行攔截。
package cn.itcast.filter;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class FilterDemo3 implements Filter{
/**
* 使用FilterConfig來獲取一些資訊
*/
public void init(FilterConfig config) throws ServletException {
// 獲取filter配置的名稱<filter-name>
String filterName = config.getFilterName();
System.out.println("過濾器的配置名稱:"+filterName);
// 獲取初始化引數
String username = config.getInitParameter("username");
String password = config.getInitParameter("password");
System.out.println(username+" : "+password);
Enumeration<String> e = config.getInitParameterNames();
while(e.hasMoreElements()){
// username和password
String name = e.nextElement();
// 獲取username的值
String value = config.getInitParameter(name);
System.out.println(name+" : "+value);
}
// 獲取ServeltContext物件
ServletContext c = config.getServletContext();
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("我是demo3...");
chain.doFilter(request, response);
System.out.println("我是回來的demo3...");
}
public void destroy() {
}
}
<filter>
<filter-name>FilterDemo3</filter-name>
<filter-class>cn.itcast.filter.FilterDemo3</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterDemo3</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
-->
對映Filter示例:
<filter-mapping>
<filter-name>testFilter</filter-name>
<url-pattern>/test.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>testFilter</filter-name>
<url-pattern>/index.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
對映Filter的多種方式:
<dispatcher> 子元素可以設定的值及其意義:
•REQUEST:當用戶直接訪問頁面時,Web容器將會呼叫過濾器。如果目標資源是通過RequestDispatcher的include()或forward()方法訪問時,那麼該過濾器就不會被呼叫。
•INCLUDE:如果目標資源是通過RequestDispatcher的include()方法訪問時,那麼該過濾器將被呼叫。除此之外,該過濾器不會被呼叫。
•FORWARD:如果目標資源是通過RequestDispatcher的forward()方法訪問時,那麼該過濾器將被呼叫,除此之外,該過濾器不會被呼叫。
•ERROR:如果目標資源是通過宣告式異常處理機制呼叫時,那麼該過濾器將被呼叫。除此之外,過濾器不會被呼叫。
Filter常見應用(1):
統一全站字元編碼的過濾器
•通過配置引數encoding指明使用何種字元編碼,以處理Html Form請求引數的中文問題
過濾器類:
web.xml配置檔案:
package cn.itcast.demo1;
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;
/**
* 解決全域性的編碼的問題
* @author Administrator
*
*/
public class EncodingFilter implements Filter{
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
/**
* 設定編碼的問題
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String encoding = filterConfig.getInitParameter("encoding");
// 解決POST亂碼的問題
request.setCharacterEncoding(encoding);
// 響應
response.setContentType("text/html;charset="+encoding);
// 放行
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Filter常見應用(2)
禁止瀏覽器快取所有動態頁面的過濾器:
•有 3 個 HTTP 響應頭欄位都可以禁止瀏覽器快取當前頁面,它們在 Servlet 中的示例程式碼如下:
•response.setDateHeader("Expires",-1);
•response.setHeader("Cache-Control","no-cache");
•response.setHeader("Pragma","no-cache");
•並不是所有的瀏覽器都能完全支援上面的三個響應頭,因此最好是同時使用上面的三個響應頭。
•Expires資料頭:值為GMT時間值,為-1指瀏覽器不要快取頁面
•Cache-Control響應頭有兩個常用值:
•no-cache指瀏覽器不要快取當前頁面。
•max-age:xxx指瀏覽器快取頁面xxx秒。
package cn.itcast.demo2;
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.HttpServletResponse;
public class TimeFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 設定三個頭資訊
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", -1);
chain.doFilter(request, resp);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
<filter>
<filter-name>TimeFilter</filter-name>
<filter-class>cn.itcast.demo2.TimeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TimeFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
</filter>
Filter常見應用(3):
分ip統計訪問次數
因為一個網站可能有多個頁面,無論哪個頁面被訪問,都要統計訪問次數,所以使用過濾器最為方便。
因為需要分IP統計,所以可以在過濾器中建立一個Map,使用IP為key,訪問次數為value。當有使用者訪問時,獲取請求的IP,如果IP在Map中存在,說明以前訪問過,那麼在訪問次數上加1,即可;IP在Map中不存在,那麼設定次數為1。
把這個Map存放到ServletContext中!
package cn.itcast.demo3;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class CountFilter implements Filter{
private FilterConfig config;
/**
* 進行初始化的操作
* 在ServletContext域中存入map
*/
public void init(FilterConfig config) throws ServletException {
// 先有一個MAP
Map<String, Integer> countMap = new HashMap<String, Integer>();
ServletContext context = config.getServletContext();
// 存
context.setAttribute("countMap", countMap);
this.config = config;
}
/**
* 該方法執行了
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
/**
* 1.獲取map
* 2.獲取ip
* 3.在map中和ip就對比
* * 如果map中有ip,獲取count,+1
* * 如果map中沒有ip,把ip和count=1存入到map中
* 4.把map存入到ServletContext中
* 5.放行
*/
ServletContext context = config.getServletContext();
// 獲取map
Map<String, Integer> countMap = (Map<String, Integer>) context.getAttribute("countMap");
// 獲取你的ip
String ip = request.getRemoteAddr();
// 判斷map中是否存在該ip
Integer count = countMap.get(ip);
// 判斷count為null
if(count == null){
// 第一次來
count = 1;
}else{
// 來過很多次了
count++;
}
// 把ip和count存入到Map中
countMap.put(ip, count);
// 向域中存入map
context.setAttribute("countMap", countMap);
// 放行
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
<filter>
<filter-name>CountFilter</filter-name>
<filter-class>cn.itcast.demo3.CountFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CountFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Filter常見應用(4):
實現使用者自動登陸的過濾器
什麼是自動登入?
自動登入就是必須先登入,並且選擇自動登入的按鈕,關閉瀏覽器,再次開啟瀏覽器訪問原來的登入頁面的時候,會直接進入,不行再次登入。
•在使用者登陸成功後,以cookis形式傳送使用者名稱、密碼給客戶端
•編寫一個過濾器,filter方法中檢查cookie中是否帶有使用者名稱、密碼資訊,如果存在則呼叫業務層登陸方法,登陸成功後則向session中存入user物件(即使用者登陸標記),以實現程式完成自動登陸。
保證環境配置準備完畢,還需要在MySQL中建立表資訊,與User封裝類的欄位相同。
後臺程式:
MyjdbcUtil.java: 操作資料庫的工具類,獨立放在util工具包中:
package cn.itcast.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* 操作JDBC
* @author Administrator
*/
public class MyJdbcUtil {
public static ComboPooledDataSource dataSource = new ComboPooledDataSource();
/**
* 獲取連結
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
/**
* 獲取連線池
* @return
*/
public static DataSource getDataSource(){
return dataSource;
}
/**
* 釋放資源
* @param rs
* @param stmt
* @param conn
*/
public static void release(ResultSet rs,Statement stmt,Connection conn){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
/**
* 釋放資源的方法
* @param stmt
* @param conn
*/
public static void release(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
// 歸還的方法
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
LoginServlet.java:
package cn.itcast.demo4;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 登陸的功能
* @author Administrator
*
*/
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = -8579027867816374707L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 獲取引數
String username = request.getParameter("username");
String password = request.getParameter("password");
UserDao dao = new UserDao();
User existUser = dao.findUser(username, password);
if(existUser == null){
// 給提示
request.getRequestDispatcher("/demo4/login.jsp").forward(request, response);
}else{
// 把使用者名稱和密碼儲存到cookie中,回寫到瀏覽器。
String autologin = request.getParameter("autologin");
// 下一次自動登陸,把你的使用者名稱和密碼儲存起來
if("auto_ok".equals(autologin)){
// 建立cookie,回寫
Cookie cookie = new Cookie("autologin",username+"#itcast#"+password);
// 設定有效時間
cookie.setMaxAge(60*60);
// 回寫
response.addCookie(cookie);
}
// 把使用者的資訊儲存到session中
request.getSession().setAttribute("existUser", existUser);
response.sendRedirect(request.getContextPath()+"/demo4/suc.jsp");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
User.java:
package cn.itcast.demo4;
public class User {
private int id;
private String username;
private String password;
private String nickname;
private String type;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
UserDao.java:
package cn.itcast.demo4;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import cn.itcast.utils.MyJdbcUtil;
public class UserDao {
/**
* 通過使用者名稱和密碼查詢單個使用者
* @param username
* @param password
* @return
*/
public User findUser(String username,String password){
QueryRunner runner = new QueryRunner(MyJdbcUtil.getDataSource());
try {
return runner.query("select * from t_user where username = ? and password = ?", new BeanHandler<User>(User.class), username,password);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
AutoLoginFilter.java: 過濾器實現自動登入功能
package cn.itcast.demo4;
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.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 自動登陸的功能
* @author Administrator
*
*/
public class AutoLoginFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
/**
* * 在過濾器中可以直接獲取session中的使用者資訊,如果user不為空,說明瀏覽器沒關。放行。
* 從session中獲取不到user的資訊
* 先獲取cookie,獲取指定名稱的cookie,
* 如果cookie為空,放行。
* 如果cookie不為空,獲取使用者名稱和密碼。去資料庫查詢。
* 如果查詢不到,cookie的資訊不正確,放行(沒有存入session中)。
* 如果查詢到了,cookie中的資訊是正確,把使用者的資訊儲存到session中。放行。
*/
// 從session中獲取使用者的資訊
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
// 從session獲取使用者的資訊
User user = (User) session.getAttribute("existUser");
// 如果瀏覽器沒關閉,session中的使用者資訊不為null的
if(user != null){
chain.doFilter(req, response);
}else{
// session中沒有使用者的資訊
// 獲取指定名稱的cookie
Cookie [] cookies = req.getCookies();
// 獲取到cookie,就可以進行判斷了
Cookie cookie = getCookieByName(cookies,"autologin");
// 如果cookie為null
// 在你的瀏覽器中,根本就沒有autologin的cookie
if(cookie == null){
// 直接放行 自己訪問suc.jsp(因為suc.jsp已經做過處理了,沒有session預設讓你去登陸)了。
chain.doFilter(req, response);
}else{
// 從cookie中獲取使用者名稱和密碼,去資料中查詢
String username = cookie.getValue().split("#itcast#")[0];
String password = cookie.getValue().split("#itcast#")[1];
// 你需要去資料庫中進行查詢
UserDao dao = new UserDao();
// 去資料庫中查詢指定名稱和密碼的使用者
User existUser = dao.findUser(username, password);
// 查詢出的使用者為null
if(existUser == null){
// 放行
chain.doFilter(req, response);
}else{
// 存入到session中
session.setAttribute("existUser", existUser);
// 放行
chain.doFilter(req, response);
}
}
}
}
public Cookie getCookieByName(Cookie [] cookies,String cookieName){
if(cookies == null){
return null;
}else{
for (Cookie cookie : cookies) {
// 判斷
if(cookie.getName().equals(cookieName)){
return cookie;
}
}
return null;
}
}
public void destroy() {
}
}
<filter>
<filter-name>AutoLoginFilter</filter-name>
<filter-class>cn.itcast.demo4.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AutoLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
前臺程式:
login.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>登陸頁面</h3>
<form action="${ pageContext.request.contextPath }/login" method="post">
使用者名稱:<input type="text" name="username" /><br/>
密碼:<input type="password" name="password" /><br/>
<input type="checkbox" name="autologin" value="auto_ok" />自動登陸<br/>
<input type="submit" value="登陸 " />
</form>
</body>
</html>
suc.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:if test="${ empty existUser }">
<h3><a href="${ pageContext.request.contextPath }/demo4/login.jsp">親,請登陸!</a></h3>
</c:if>
<c:if test="${ not empty existUser }">
<h3>親!歡迎訪問:${ existUser.nickname },角色是:${ existUser.type }</h3>
<h3>
<a href="${ pageContext.request.contextPath }/admin/add.jsp">新增商品</a>
<a href="${ pageContext.request.contextPath }/admin/update.jsp">修改商品</a>
<a href="${ pageContext.request.contextPath }/admin/delete.jsp">刪除商品</a>
<a href="${ pageContext.request.contextPath }/user/show.jsp">檢視商品</a>
</h3>
</c:if>
</body>
</html>
Filter常見應用(5):
AuthorityFilter 許可權過濾器
在一個系統中通常有多個許可權的使用者。不同許可權使用者的可以瀏覽不同的頁面。使用Filter進行判斷不僅省下了程式碼量,而且如果要更改的話只需要在Filter檔案裡動下就可以。
過濾器類:
package cn.itcast.demo5;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import cn.itcast.demo4.User;
/**
* URL級別全選驗證
* @author Administrator
*
*/
public class CheckUserUrlFilter implements Filter{
private FilterConfig config;
/**
* 初始化的操作,獲取初始化引數,讀取到Map集合中。存入ServletContext中。
*/
public void init(FilterConfig config) throws ServletException {
// 建立Map儲存資訊
Map<String, String> urlMap = new HashMap<String, String>();
// 獲取初始化引數,把引數的內容儲存到urlMap中
Enumeration<String> e = config.getInitParameterNames();
while(e.hasMoreElements()){
// 獲取到<param-name>的值
String paramName = e.nextElement();
// 獲取到的是<param-value>的值
String paramValue = config.getInitParameter(paramName);
// 存入Map集合中 {/admin:admin} {/user:user}
urlMap.put(paramName, paramValue);
}
// 存入到ServletContext中
config.getServletContext().setAttribute("urlMap", urlMap);
this.config = config;
}
/**
* 許可權的驗證
* * 從請求的連結中拿到連結和urlMap中的連結和type型別做對比
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 先獲取請求的連結 請求:/admin/add.jsp map {/admin:admin} {/user:user}
HttpServletRequest req = (HttpServletRequest) request;
// /day21/admin/add.jsp
// String uri = req.getRequestURI();
// System.out.println(uri);
//
// 獲取請求的連結
String sPath = req.getServletPath();
// System.out.println(sPath);
// 和Map來進行對比
ServletContext context = config.getServletContext();
// 獲取Map的集合了
Map<String, String> urlMap = (Map<String, String>) context.getAttribute("urlMap");
// 做對比 set的key值 /admin /user
Set<String> set = urlMap.keySet();
// 迴圈 /admin
for (String paramName : set) {
// /admin/add.jsp從/admin開始的
if(sPath.startsWith(paramName)){
// 去獲取session的使用者資訊,從使用者的資訊中獲取type屬性的值,和Map中的value的值進行對比。
User user = (User) req.getSession().getAttribute("existUser");
// 如果user為null
if(user == null){
// 轉發到登陸的頁面上
req.getRequestDispatcher("/demo4/login.jsp").forward(req, response);
return;
}else{
// 去判斷使用者的type的值和我Map中的value的值做對比
String userType = user.getType();
// 還有一個Map中存了一個值 {/admin admin} {/user user}
String paramValue = urlMap.get(paramName);
if(userType.equals(paramValue)){
// 匹配成功了 /admin/add.jsp 做過一次判斷了,已/admin開頭的,
// 從使用者的資訊中獲取type 的值和Map的值做了對比
chain.doFilter(req, response);
return;
}else{
// 向用戶提示
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("<h3>親,您許可權不夠!!</h3>");
return;
}
}
}
}
chain.doFilter(req, response);
}
public void destroy() {
}
}
web.xml配置檔案:
<filter>
<filter-name>CheckUserUrlFilter</filter-name>
<filter-class>cn.itcast.demo5.CheckUserUrlFilter</filter-class>
<init-param>
<param-name>/admin</param-name>
<param-value>admin</param-value>
</init-param>
<init-param>
<param-name>/user</param-name>
<param-value>user</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CheckUserUrlFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Filter高階開發:
由於開發人員在filter中可以得到代表使用者請求和響應的request、response物件,因此在程式設計中可以使用Decorator(裝飾器)模式對request、response物件進行包裝,再把包裝物件傳給目標資源,從而實現一些特殊需求。
Decorator設計模式的實現
•1.首先看需要被增強物件繼承了什麼介面或父類,編寫一個類也去繼承這些介面或父類。
•2.在類中定義一個變數,變數型別即需增強物件的型別。
•3.在類中定義一個建構函式,接收需增強的物件。
•4.覆蓋需增強的方法,編寫增強的程式碼。
request物件的增強:
Servlet API 中提供了一個request物件的Decorator設計模式的預設實現類HttpServletRequestWrapper , (HttpServletRequestWrapper 類實現了request 介面中的所有方法,但這些方法的內部實現都是僅僅呼叫了一下所包裝的的 request 物件的對應方法)以避免使用者在對request物件進行增強時需要實現request介面中的所有方法。
使用Decorator模式包裝request物件,完全解決get、post請求方式下的亂碼問題