JAVA EE(七) —— 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);
			}
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章