文章目錄
- 一、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
-
:表示攔截一個指定url的請求,一般用于比較重要的servlet針對性的攔截/test.do
-
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);
}
}
}
}