天天看點

JAVA EE(七) —— Filter 過濾器一、Filter 過濾器概述二、Filter過濾器示例

文章目錄

  • 一、Filter 過濾器概述
    • 1、Filter 介紹
    • 2、常見過濾器
    • 3、Filter 配置檔案
    • 4、Filter 執行過程
  • 二、Filter過濾器示例

一、Filter 過濾器概述

1、Filter 介紹

(1)介紹

  • Filter也稱之為過濾器,它是Servlet技術中最實用的技術,Web開發人員通過Filter技術,對web伺服器管理的所有web資源如Jsp、Servlet、靜态圖檔檔案或靜态 html 檔案等進行攔截,進而實作一些特殊的功能。例如實作URL級别的權限通路控制、過濾敏感詞彙、壓縮響應資訊等一些進階功能。
  • 通常使用過濾器Filter來做登入驗證、會話管理、日志記錄以及一些通用的操作,過濾器需要實作Filter接口
  • Filter過濾器的執行順序是在Servlet之前的,其生命周期和Servlet類似

    建立 => 初始化 => 過濾 => 銷毀

(2)Filter 的作用

  • 在用戶端的請求通路後端資源之前,攔截這些請求。
  • 在伺服器的響應發送回用戶端之前,處理這些響應。

2、常見過濾器

  • 身份驗證過濾器(Authentication Filters)。
  • 資料壓縮過濾器(Data compression Filters)。
  • 加密過濾器(Encryption Filters)。
  • 觸發資源通路事件過濾器。
  • 圖像轉換過濾器(Image Conversion Filters)。
  • 日志記錄和稽核過濾器(Logging and Auditing Filters)。
  • MIME-TYPE 鍊過濾器(MIME-TYPE Chain Filters)。
  • 标記化過濾器(Tokenizing Filters)。
  • XSL/T 過濾器(XSL/T Filters),轉換 XML 内容。

3、Filter 配置檔案

<filter>
	<filter-name></filter-name>
	<filter-class></filter-class>
</filter->
<filter-mapping>
	<filter-name></filter-name>
	<url-pattern><filter-pattern>
</filter-mapping>
           
  • filter-name:過濾器類的名稱,如

    MyFilter

  • filter-class:過濾器類的路徑,如

    com.java.filter.MyFilter

  • url-pattern:攔截過濾規則,如
    • /*

      :表示攔截所有
    • *.do

      :表示以

      .do

      結尾的都會被攔截進入過濾器,一般用于某個子產品的攔截處理
    • /test.do

      :表示攔截一個指定url的請求,一般用于比較重要的servlet針對性的攔截

4、Filter 執行過程

  • 用戶端發起請求到伺服器;
  • 伺服器接收到請求後,根據URI資訊在web.xml中找到對應的過濾器執行doFilter方法,該方法對此次請求進行處理後如果符合要求則放行;
  • 放行後如果還有符合要求的過濾則繼續進行過濾,找到執行對應的servlet進行請求處理;
  • servlet對請求處理完畢後,也就service方法結束後,還需繼續傳回相應的doFilter方法繼續執行。

二、Filter過濾器示例

1、過濾器完成多點登陸的問題

(1)使用session完成會話跟蹤

  • 在使用者沒有登陸的情況下,不允許通路一些網頁和資源
  • 使用者登陸成功以後,将使用者資訊儲存在session對象中
  • 通過登陸過濾器判斷使用者是否已經登陸,如果登陸放行,否則就沖定向到登陸網頁
  • 不需要登陸也能通路的資源

(2)使用Cookie記錄登陸使用者狀态實作自動登陸

  • 在使用者登陸成功以後,生成一個不可重複的随機字元串作為使用者的登陸令牌資訊
  • 将該令牌資訊儲存到資料庫該使用者的資料中
  • 使用Cookie将該令牌資訊記錄到浏覽器
  • 登陸過濾器中擷取(token)Cookie,通過token從資料庫中擷取使用者資料,再将使用者資料保

(3)解決多點登陸的問題

2、代碼

(1)model 層

① users 類的失血模型

public class Users {
	private int userId;
	private String userAccount;
	private String userPassWord;
	private String userTaken;
	
	//省略getter、setter
}
           

(2)util 層

① JDBCUtil 類:連接配接資料庫

public class JDBCUtil {
	public static final ThreadLocal<Connection> threadLocal=new ThreadLocal<Connection>();
	private static String driver = null;
	private static String url = null;
	private static String user = null;
	private static String password = null;
	static Scanner sc = new Scanner(System.in);
	
	//靜态代碼塊
	static{
		try {
			//擷取檔案流
			InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("user.properties");
			//建立properties對象
			Properties prop = new Properties();
			//使用prop對象的load()方法加載流
			prop.load(is);
			//擷取其檔案的資料
			driver = prop.getProperty("river");
			url = prop.getProperty("url");
			user = prop.getProperty("user");
			password = prop.getProperty("password");
			
			//注冊驅動
			Class.forName(driver);
		} catch (Exception  e) {
			e.printStackTrace();
		}
	}
	
	//擷取連接配接
	public  static Connection registJDBC() throws Exception {
		return DriverManager.getConnection(url, user, password);
	}
	
	//關流
	public static void close(ResultSet resultSet, PreparedStatement ps, Connection conn) {
		if(resultSet != null) {
			try {
				resultSet.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if(ps != null) {
			try {
				ps.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
           

② FinalDates 類:靜态常量

public class FinalDates {
	public static final String USERS = "users";
	public static final String TAKEN="user_taken";
}
           

(3)service 層

① UserService 類:UserService 的接口類

public interface UserService {
	//定義Service層查詢使用者賬戶的方法
	public Users readAccount(String user_Account);
	//定義Service層修改使用者登入令牌的方法
	public void updateTaken(int user_Id, String user_Taken);
	//定義Service層查詢使用者登入令牌的方法
	public Users readTaken(String user_Taken);
}
           

② Imp_UserService 類:實作 UserService 接口

public class Imp_UserService implements UserService{
	private UserDao userDao = new Imp_UserDao();
	@Override
	public Users readAccount(String user_Account) {
		Users users = null;
		try {
			users = userDao.readAccount(user_Account);
		}catch(Exception e) {
			e.printStackTrace();
		}
		return users;
	}

	@Override
	public void updateTaken(int user_Id, String user_Taken) {
		Connection conn=null;
		try {
			conn = JDBCUtil.registJDBC();
			
			//關閉自動送出
			conn.setAutoCommit(false);
			JDBCUtil.threadLocal.set(conn);
			
			userDao.updateTaken(user_Id, user_Taken);
			
			//送出事務
			conn.commit();
		} catch (Exception e) {
			try {
				//復原事務
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}
	
	@Override
	public Users readTaken(String user_Taken) {
		Users users = null;
		try {
			users = userDao.readTaken(user_Taken);
		}catch(Exception e) {
			e.printStackTrace();
		}
		return users;
	}
}
           

(4)dao 層

① UserDao 類:UserDao 的接口類

public interface UserDao {
	//定義Dao層一個查詢使用者賬戶的方法
	public Users readAccount(String user_Account) throws Exception;
	//定義Dao層一個修改使用者登入令牌的方法
	public void updateTaken(int user_Id,String user_Taken) throws Exception;
	//定義Dao層一個查詢使用者登入令牌的方法
	public Users readTaken(String user_Taken) throws Exception;
}
           

② Imp_UserDao 類:實作 UserDao 接口

public class Imp_UserDao implements UserDao{
	@Override
	public Users readAccount(String user_Account) throws Exception {
		Connection conn = JDBCUtil.registJDBC();
		String sql = "SELECT user_id,user_account,user_password,user_taken FROM users WHERE user_account = ?";
		PreparedStatement ps = conn.prepareStatement(sql);
		ps.setString(1, user_Account);
		ResultSet resultSet = ps.executeQuery();
		
		Users users = null;
		if(resultSet.next()) {
			users = new Users();
			users.setUserId(resultSet.getInt("user_id"));
			users.setUserAccount(resultSet.getString("user_account"));
			users.setUserPassWord(resultSet.getString("user_password"));
			users.setUserTaken(resultSet.getString("user_taken"));
		}
		JDBCUtil.close(resultSet, ps, conn);
		return users;
	}

	@Override
	public void updateTaken(int user_Id, String user_Taken) throws Exception {
		Connection conn = JDBCUtil.threadLocal.get();
		String sql = "UPDATE users SET user_taken = ? WHERE user_id = ?";
		PreparedStatement ps = conn.prepareStatement(sql);
		ps.setString(1, user_Taken);
		ps.setInt(2, user_Id);
		ps.executeUpdate();
		ps.close();
	}

	@Override
	public Users readTaken(String user_Taken) throws Exception {
		Connection conn = JDBCUtil.registJDBC();
		String sql = "SELECT user_id, user_account, user_password, user_taken FROM users WHERE user_taken = ?";
		PreparedStatement ps = conn.prepareStatement(sql);
		ps.setString(1, user_Taken);
		ResultSet resultSet = ps.executeQuery();
		Users users = null;
		if(resultSet.next()) {
			users=new Users();
			users.setUserId(resultSet.getInt("user_id"));
			users.setUserAccount(resultSet.getString("user_account"));
			users.setUserPassWord(resultSet.getString("user_password"));
			users.setUserTaken(resultSet.getString("user_taken"));
		}
		JDBCUtil.close(resultSet, ps, conn);
		return users;
	}
}
           

(5)filter 層

① LoginFilter 類:登入的過濾器操作

建立的過濾器類需要實作Filter接口,并且重寫doFilter方法

@WebFilter("/*")
public class LoginFilter implements Filter{
	private List<String> starts = new ArrayList<String>();
	private List<String> ends = new ArrayList<String>();
	
	@Override
	public void init(FilterConfig config) throws ServletException {
		//存入字尾需要放行的位址
		ends.add("css");
		ends.add("js");
		ends.add("jpg");
		ends.add("png");
		ends.add("gif");
		
		//存入字首需要放行的位址
		starts.add("login");
		starts.add("loginservlet");
		starts.add("LoginServlet");
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException {
		//設定編碼格式
		req.setCharacterEncoding("utf-8");
		resp.setContentType("text/html;charset=utf-8");
		//擷取本次請求的請求路徑
		HttpServletRequest request = (HttpServletRequest)req;
		String uri = request.getRequestURI().toLowerCase();
		//提取出字首和字尾
		String resourceURI = uri.substring(uri.lastIndexOf("/") + 1 );
		String start = null;
		String end = null;
		int index = resourceURI.lastIndexOf(".");
		if(index != -1) {
			start = resourceURI.substring(0, index);
			end = resourceURI.substring(index + 1);
		}else {
			start = resourceURI;
		}
		
		//判斷
		if(starts.contains(start) || ends.contains(end)) {
			System.out.println("進來0");
			//滿足條件放行
			chain.doFilter(req, resp);
		}else {
			//判斷浏覽器發送的請求中的cookie資訊是否包含taken
			Cookie[] cookies = request.getCookies();
			String taken = null;
			if(cookies != null) {
				for(Cookie cookie: cookies) {
					if(cookie.getName().equals(FinalDates.TAKEN)) {
						taken = cookie.getValue();
					}
				}
			}
			if(taken == null) {
				HttpServletResponse response=(HttpServletResponse)resp;
//				chain.doFilter(req, resp);
				response.sendRedirect("/GouYaoDemo/jsp/login.jsp");
			}else {
				UserService userService = new Imp_UserService();
				Users users = userService.readTaken(taken);
				if(users != null) {
					HttpSession session = request.getSession();
					session.setAttribute(FinalDates.USERS, users);
					chain.doFilter(req, resp);
				}else {
					String message = "您的賬号在别處登入";
					request.setAttribute("message", message);
					request.getRequestDispatcher("/jsp/login.jsp").forward(request, resp);
				}
			}
		}
	}
	
	@Override
	public void destroy() {
	}
}
           
  • init():初始化,在這裡可以配置允許直接放行的檔案類型等,在伺服器啟動就會執行。
  • doFilter():這個時攔截請求的方法,在這個方法裡,了可以對資源進行管理和配置設定,

    chain.doFilter()

    表示手動對滿足條件的請求進行放行。
  • destroy():銷毀,在伺服器關閉的時候執行。

(6)servlet 層

① LoginServlet 類:使用者登入操作類(包括使用者登入資訊判斷等)

@WebServlet("/loginServlet")
public class LoginServlet extends  HttpServlet{
	@Override
	public void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//處理亂碼
		request.setCharacterEncoding("utf-8");

		//擷取輸入框的資料
		String userName = request.getParameter("username");
		String passWord = request.getParameter("password");
		//定義一個字元串用于給使用者回報登入情況資訊
		String message = null;
		//防止使用者直接輸入位址通路,在後端設定使用者名和密碼為空的判斷
		//判斷使用者名是否為空
		if(userName == null || "".equals(userName)) {
			message = "使用者名不能為空";
			request.setAttribute("message", message);
			//使用者名為空的情況下再次請求轉發到登入界面
			request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
			return;
		}
		//判斷密碼是否為空
		if(passWord == null || "".equals(passWord)) {
			message = "密碼不能為空";
			request.setAttribute("message", message);
			//密碼為空的情況下再次請求轉發到登入界面
			request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
			return;
		}
		
		//判斷使用者賬号是否存在以及賬号密碼是否正确
		UserService userService = new Imp_UserService();
		//通過service層調用dao層的查詢用于賬戶的方法,并傳回一個Users對象
		Users users = userService.readAccount(userName);
		if(users == null) {
			message = "使用者名不存在";
			request.setAttribute("message", message);
			request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
		}else {
			if(!passWord.equals(users.getUserPassWord())) {
				message = "密碼錯誤";
				request.setAttribute("message", message);
				request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
			}else {
				/*
				 * 使用者賬号密碼都正确,将使用者對象儲存到Session中,
				 * 并随機生成一個使用者登入令牌存入到資料庫和浏覽器中,
				 * 最後跳轉到另一個頁面,
				 */
				request.getSession().setAttribute(FinalDates.USERS, users);
				
				String userTaken = UUID.randomUUID().toString().replace("-", "");
				//将登入令牌更新到資料庫中的使用者資料
				userService.updateTaken(users.getUserId(), userTaken);
				//将登入令牌儲存到浏覽器中
				Cookie cookie = new Cookie(FinalDates.TAKEN, userTaken);
				cookie.setMaxAge(60*60*7);
				//将Cookie響應到浏覽器
				response.addCookie(cookie);
				//請求轉發到另一個網頁
				request.getRequestDispatcher("/jsp/main.jsp").forward(request, response);
			}
		}
	}
}