1、Filter簡介
(1)Filter也稱之爲過濾器,它是Servlet技術中最實用的技術,WEB開發人員通過Filter技術,對web服務器管理的所有web資源:例如Jsp, Servlet, 靜態圖片文件或靜態 html 文件等進行攔截,從而實現一些特殊的功能。例如實現URL級別的權限訪問控制、過濾敏感詞彙、壓縮響應信息等一些高級功能。
(2)Servlet API中提供了一個Filter接口,開發web應用時,如果編寫的Java類實現了這個接口,則把這個java類稱之爲過濾器Filter。通過Filter技術,開發人員可以實現用戶在訪問某個目標資源之前,對訪問的請求和響應進行攔截,如下所示:
2、Filter是如何實現攔截的?
Filter接口中有一個doFilter方法,當開發人員編寫好Filter,並配置對哪個web資源(攔截url)進行攔截後,WEB服務器每次在調用web資源之前,都會先調用一下filter的doFilter方法,因此,在該方法內編寫代碼可達到如下目的:
調用目標資源之前,讓一段代碼執行
是否調用目標資源(即是否讓用戶訪問web資源)。
web服務器在調用doFilter方法時,會傳遞一個filterChain對象進來,filterChain對象是filter接口中最重要的一個對象,它也提供了一個doFilter方法,開發人員可以根據需求決定是否調用此方法,調用該方法,則web服務器就會調用web資源的service方法,即web資源就會被訪問,否則web資源不會被訪問。
調用目標資源之後,讓一段代碼執行
3、Filter開發入門
(1)Filter開發分爲二個步驟:
編寫java類實現Filter接口,並實現(三個方法)其doFilter方法。
在 web.xml 文件中使用<filter>和<filter-mapping>元素對編寫的filter類進行註冊,並設置它所能攔截的資源。
(2)Filter鏈 ---
在一個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文檔)
4、Filter的生命週期
(1)init(FilterConfig filterConfig)throws ServletException:
和我們編寫的Servlet程序一樣,Filter的創建和銷燬由WEB服務器負責。 web 應用程序啓動時,web 服務器將創建Filter 的實例對象,並調用其init方法進行初始化(注:filter對象只會創建一次,init方法也只會執行一次。示例 )
開發人員通過init方法的參數,可獲得代表當前filter配置信息的FilterConfig對象。
(2)doFilter(ServletRequest,ServletResponse,FilterChain)
每次filter進行攔截都會執行
在實際開發中方法中參數request和response通常轉換爲HttpServletRequest和HttpServletResponse類型進行操作
(3)destroy():
在Web容器卸載 Filter 對象之前被調用。
package com.itheima.filter;
import java.io.IOException;
import java.util.Enumeration;
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 com.sun.net.httpserver.Filter.Chain;
public class Filter1 implements Filter {
public Filter1() {
System.out.println("Filter被創建出來了。。。");
}
public void destroy() {
System.out.println("destory......");
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("dofilter....");
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init.....");
String value1 = filterConfig.getInitParameter("param1");
System.out.println(value1);
Enumeration enumeration =filterConfig.getInitParameterNames();
while(enumeration.hasMoreElements()){
String name = (String) enumeration.nextElement();
String value = filterConfig.getInitParameter(name);
System.out.println(name+":"+value);
}
filterConfig.getServletContext();
}
}
5、FilterConfig接口
(1)用戶在配置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上下文對象的引用。
(2)FilterConfig 提供參數,是Filter類私有參數,Filter2的初始化參數,不能在Filter1 中進行獲取
(3) 配置全局參數,<context-param> 進行配置,通過ServletContext 獲得
(4)實驗:得到filter配置信息
6、註冊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接口對象來訪問初始化參數。
7、映射Filter
<filter-mapping>元素用於設置一個 Filter 所負責攔截的資源。一個Filter攔截的資源可通過兩種方式來指定:Servlet 名稱和資源訪問的請求路徑
(1)<filter-name>子元素用於設置filter的註冊名稱。該值必須是在<filter>元素中聲明過的過濾器的名字
(2)<url-pattern>設置 filter 所攔截的請求路徑(過濾器關聯的URL樣式)
(3)<servlet-name>指定過濾器所攔截的Servlet名稱。
(4)<dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默認REQUEST。用戶可以設置多個<dispatcher> 子元素用來指定 Filter 對資源的多種調用方式進行攔截。
(5)<filter-mapping> 過濾器攔截配置
如果連接目標資源是一個Servlet,可以選擇url和servlet名稱兩種配置方式
<!-- 攔截/hello是Servlet 路徑 -->
<url-pattern>/hello</url-pattern>
<!-- 攔截Servlet 還可以通過Servlet 名稱進行攔截 -->
<servlet-name>HelloServlet</servlet-name>
(6)url-pattern 和 Servlet中路徑寫法一樣,有三種 : 完全匹配、目錄匹配、擴展名匹配
(7)<dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式
容器調用服務器端資源 有四種方式
REQUEST、FORWARD、INCLUDE、ERROR
8、映射Filter的多種方式
<dispatcher> 子元素可以設置的值及其意義:
REQUEST:當用戶直接訪問頁面時,Web容器將會調用過濾器。如果目標資源是通過RequestDispatcher的include()或forward()方法訪問時,那麼該過濾器就不會被調用。
INCLUDE:如果目標資源是通過RequestDispatcher的include()方法訪問時,那麼該過濾器將被調用。除此之外,該過濾器不會被調用。
FORWARD:如果目標資源是通過RequestDispatcher的forward()方法訪問時,那麼該過濾器將被調用,除此之外,該過濾器不會被調用。
ERROR:如果目標資源是通過聲明式異常處理機制調用時,那麼該過濾器將被調用。除此之外,過濾器不會被調用
9、映射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>
10、Filter常見應用
(1)統一全站字符編碼的過濾器
通過配置參數encoding指明使用何種字符編碼,以處理Html Form請求參數的中文問題
案例:編寫jsp 輸入用戶名,在Servlet中獲取用戶名,將用戶名輸出到瀏覽器上
處理請求post亂碼代碼
request.setCharacterEncoding("utf-8");
設置響應編碼集代碼
response.setContentType("text/html;charset=utf-8");
經常會使用,而過濾器可以在目標資源之前執行,將很多程序中處理亂碼公共代碼,提取到過濾器中 ,以後程序中不需要處理編碼問題了
package com.itheima.filter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
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.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class EncodingFilter implements Filter {
private FilterConfig config = null;
public void destroy() {
}
/**
* 全站亂碼解決
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
chain.doFilter(new MyRequest(req), response);
}
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request = null;
private boolean isEncode = false;
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public Map getParameterMap() {
if (request.getMethod().equalsIgnoreCase("post")) {
try {
request.setCharacterEncoding(config.getInitParameter("encode"));
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (request.getMethod().equalsIgnoreCase("get")) {
try {
Map<String, String[]> map = request.getParameterMap();
if(!isEncode){
for (String key : map.keySet()) {
String [] vs = map.get(key);
for(int i=0;i<vs.length;i++){
String v = vs[i];
v = new String(v.getBytes("iso8859-1"), config.getInitParameter("encode"));
vs[i] = v;
}
}
isEncode = true;
}
return map;
} catch (Exception e) {
e.printStackTrace();
}
}
return super.getParameterMap();
}
@Override
public String[] getParameterValues(String name) {
if(getParameterMap().get(name)==null)return null;
return (String[])getParameterMap().get(name);
}
@Override
public String getParameter(String name) {
if(getParameterMap().get(name)==null)return null;
return ((String[])getParameterMap().get(name))[0];
}
}
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
}
}
package com.itheima.web;
import java.io.IOException;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EncodeTestServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String vs = request.getParameterValues("username")[0];
System.out.println(vs);
String value = request.getParameter("username");
System.out.println(value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
<filter>
<filter-name>encode</filter-name>
<filter-class>com.itheima.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encode</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(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 com.itheima.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;
import javax.servlet.http.HttpServletResponse;
public class NoChachFilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setDateHeader("Expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
<filter>
<filter-name>nocach</filter-name>
<filter-class>com.itheima.filter.NoChachFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>nocach</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
(3)控制瀏覽器緩存頁面中的靜態資源的過濾器:
場景:有些動態頁面中引用了一些圖片或css文件以修飾頁面效果,這些圖片和css文件經常是不變化的,所以爲減輕服務器的壓力,可以使用filter控制瀏覽器緩存這些文件,以提升服務器的性能。
Tomcat緩存策略
對於服務器端經常不變化文件,設置客戶端緩存時間,在客戶端資源緩存時間到期之前,就不會去訪問服務器獲取該資源 -------- 比tomcat內置緩存策略更優手段
* 減少服務器請求次數,提升性能
設置靜態資源緩存時間,需要設置 Expires 過期時間 ,在客戶端資源沒有過期之前,不會產生對該資源的請求的
* 設置Expires 通常使用 response.setDateHeader 進行設置 設置毫秒值
package com.itheima.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;
import javax.servlet.http.HttpServletResponse;
public class CacheFilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setDateHeader("Expires", System.currentTimeMillis()+3600l*24*30*1000);
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
<filter>
<filter-name>cache</filter-name>
<filter-class>com.itheima.filter.CacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cache</filter-name>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.gif</url-pattern>
<url-pattern>*.png</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
(4)實現用戶自動登陸的過濾器
在用戶登陸成功後,以cookis形式發送用戶名、密碼給客戶端
編寫一個過濾器,filter方法中檢查cookie中是否帶有用戶名、密碼信息,如果存在則調用業務層登陸方法,登陸成功後則向session中存入user對象(即用戶登陸標記),以實現程序完成自動登陸。
在訪問一個站點,登陸時勾選自動登陸(三個月內不用登陸),操作系統後,關閉瀏覽器;過幾天再次訪問該站點時,直接進行登陸後狀態
在數據庫中創建 user表
create table user (
id int primary key auto_increment,
username varchar(20),
password varchar(40),
role varchar(10)
);
insert into user values(null,'admin','123','admin');
insert into user values(null,'aaa','123','user');
insert into user values(null,'bbb','123','user');
自動登陸 :未登錄、存在自動登陸信息、自動登陸信息正確
在用戶完成登陸後,勾選自動登陸複選框,服務器端將用戶名和密碼 以Cookie形式,保存在客戶端 。當用戶下次訪問該站點,AutoLoginFilter 過濾器從Cookie中獲取 自動登陸信息
1、判斷用戶是否已經登陸,如果已經登陸,沒有自動登陸的必要
2、判斷Cookie中是否含有自動登陸信息 ,如果沒有,無法完成自動登陸
3、使用cookie用戶名和密碼 完成自動登陸
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<form action="${pageContext.request.contextPath }/servlet/LoginServlet2" method="post">
用戶名<input type="text" name="username"/>
密碼<input type="text" name="password"/>
<input type="checkbox" name="autoLogin" value="true" />一個月內自動登錄
<input type="submit" value="登錄"/>
</form>
</body>
</html>
package com.itheima.autologin;
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;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import com.itheima.domain.User;
import com.itheima.util.DaoUtil;
import com.itheima.util.MD5Util;
public class LoginServlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try{
//1.校驗用戶名密碼是否正確
QueryRunner runner = new QueryRunner(DaoUtil.getSource());
User user = runner.query("select * from user where username=? and password=?", new BeanHandler<User>(User.class),request.getParameter("username"),MD5Util.md5(request.getParameter("password")));
if(user == null){
request.setAttribute("msg", "用戶名密碼不正確!!");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}else{
//用戶名密碼都正確,在session域中保存用戶的登錄狀態
request.getSession().setAttribute("user", user);
//如果勾選過一個月內自動登錄,發送cookie信息給瀏覽器,使瀏覽器保存用戶名密碼一個月
if(request.getParameter("autoLogin")!=null){
Cookie cookie = new Cookie("autologin",user.getUsername()+":"+user.getPassword());
cookie.setMaxAge(3600*24*30);
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
}
response.sendRedirect(request.getContextPath()+"/autologin/homepage.jsp");
}
}catch (Exception e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<c:if test="${sessionScope.user == null}">
歡迎光臨遊客!<a href="${pageContext.request.contextPath }/autologin/login.jsp">登錄</a>
</c:if>
<c:if test="${sessionScope.user != null}">
歡迎回來
</c:if>
</body>
</html>
package com.itheima.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;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import com.itheima.domain.User;
import com.itheima.util.DaoUtil;
public class AutoLoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
//1.檢查用戶是否已經登錄
if(req.getSession(false) == null || req.getSession().getAttribute("user")==null){
//2.如果用戶沒有登錄過,則檢查是否帶了autologincookie
Cookie [] cs = req.getCookies();
Cookie findc = null;
if(cs!=null){
for(Cookie c : cs){
if(c.getName().equals("autologin")){
findc = c;
break;
}
}
}
if(findc != null){
//3.如果有autologin cookie,獲取cookie的值,檢查用戶名密碼是否正確
String username = findc.getValue().split(":")[0];
String password = findc.getValue().split(":")[1];
//4.如果用戶名密碼都正確,則自動登錄一把
try{
QueryRunner runner = new QueryRunner(DaoUtil.getSource());
User user = runner.query("select * from user where username=? and password=?", new BeanHandler<User>(User.class),username,password);
if(user!=null){
req.getSession().setAttribute("user", user);
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
//5.放行資源
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
<filter>
<filter-name>autologinFilter</filter-name>
<filter-class>com.itheima.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>autologinFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(5)使用Filter實現URL級別的權限認證
(1)情景:在實際開發中我們經常把一些執行敏感操作的servlet映射到一些特殊目錄中,並用filter把這些特殊目錄保護起來,限制只能擁有相應訪問權限的用戶才能訪問這些目錄下的資源。從而在我們系統中實現一種URL級別的權限功能。
要求:爲使Filter具有通用性,Filter保護的資源和相應的訪問權限通過filter參數的形式予以配置。
(2)系統中存在很多資源,將需要進行權限控制的資源,放入特殊路徑中,編寫過濾器管理訪問特殊路徑的請求,如果沒有相應身份和權限,控制無法訪問
認證:who are you ? 用戶身份的識別 ------------ 登陸功能
權限:以認證爲基礎 what can you do ? 您能做什麼? 必須先登陸,纔有身份,有了身份,才能確定可以執行哪些操作
package com.itheima.filter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
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.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.itheima.domain.User;
public class PrivilegeFilter implements Filter {
private FilterConfig config = null;
private Map<String, String> map = new HashMap<String, String>();
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//1.當前訪問的資源是否需要權限(當前訪問資源路徑是否是map中具有的需要權限控制的路徑的子路徑)
HttpServletRequest req = (HttpServletRequest) request;
String uri = req.getRequestURI();
uri = uri.substring(config.getServletContext().getContextPath().length());
String privilege = null;
for(String name : map.keySet()){
if(uri.startsWith(name)){
privilege = map.get(name);
}
}
if(privilege == null){
//2.如果不需要權限,直接放行
chain.doFilter(request, response);
return;
}else{//3.如果需要權限,判斷當前用戶具有的權限和訪問該資源需要的權限是否相匹配,如果匹配就放行,如果不匹配則提示
HttpSession session = req.getSession(false);
if(session == null || session.getAttribute("user")==null){
throw new RuntimeException("請先登錄");
}
User user = (User) session.getAttribute("user");
if(user.getRole().equals(privilege)){
chain.doFilter(request, response);
return;
}else{
throw new RuntimeException("沒有對應的權限!!!!");
}
}
}
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
Enumeration enumeration = config.getInitParameterNames();
while(enumeration.hasMoreElements()){
String name = (String) enumeration.nextElement();
String value = config.getInitParameter(name);
map.put(name, value);
}
}
}
<filter>
<filter-name>PrivilegeFilter</filter-name>
<filter-class>com.itheima.filter.PrivilegeFilter</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>PrivilegeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
11、MD5加密
/**
* 使用md5的算法進行加密
*/
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(
plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("沒有md5這個算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
如果將用戶密碼保存在cookie文件中,非常不安全的 ,通常情況下密碼需要加密後才能保存到客戶端
* 使用md5算法對密碼進行加密
* md5 加密算法是一個單向加密算法 ,支持明文---密文 不支持密文解密
MySQL數據庫中提供md5 函數,可以完成md5 加密
mysql> select md5('123');
+----------------------------------+
| md5('123') |
+----------------------------------+
| 202cb962ac59075b964b07152d234b70 |
+----------------------------------+
解密後結果是32位數字 16進製表示
Java中提供類 MessageDigest 完成MD5加密
------------------------------------------------------------------
將數據表中所有密碼 變爲密文 update user set password = md5(password) ;
在Demo4Servlet 登陸邏輯中,對密碼進行md5 加密
在AutoLoginFilter 因爲從Cookie中獲得就是加密後密碼,所以登陸時無需再次加密
------------------------------------------------------------------
MD5 在2004 年被王小云破解,md5算法是多對一加密算法,出現兩個加密後相同密文的明文很難發現 ,王小云並沒有研究出md5 解密算法,研究出一種提高碰撞概率的算法
12、Filter高級開發
(1)由於開發人員在filter中可以得到代表用戶請求和響應的request、response對象,因此在編程中可以使用Decorator(裝飾器)模式對request、response對象進行包裝,再把包裝對象傳給目標資源,從而實現一些特殊需求。
(2)Decorator設計模式的實現
1.首先看需要被增強對象繼承了什麼接口或父類,編寫一個類也去繼承這些接口或父類。
2.在類中定義一個變量,變量類型即需增強對象的類型。
3.在類中定義一個構造函數,接收需增強的對象。
4.覆蓋需增強的方法,編寫增強的代碼。
(3)Decorator模式
1、包裝類需要和被包裝對象 實現相同接口,或者繼承相同父類
2、包裝類需要持有 被包裝對象的引用
在包裝類中定義成員變量,通過包裝類構造方法,傳入被包裝對象
3、在包裝類中,可以控制原來那些方法需要加強
不需要加強 ,調用被包裝對象的方法
需要加強,編寫增強代碼邏輯
ServletRequestWrapper 和 HttpServletRequestWrapper 提供對request對象進行包裝的方法,但是默認情況下每個方法都是調用原來request對象的方法,也就是說包裝類並沒有對request進行增強
在這兩個包裝類基礎上,繼承HttpServletRequestWrapper ,覆蓋需要增強的方法即可
13、request對象的增強
(1)Servlet API 中提供了一個request對象的Decorator設計模式的默認實現類HttpServletRequestWrapper , (HttpServletRequestWrapper 類實現了request 接口中的所有方法,但這些方法的內部實現都是僅僅調用了一下所包裝的的 request 對象的對應方法)以避免用戶在對request對象進行增強時需要實現request接口中的所有方法。
(2)使用Decorator模式包裝request對象,完全解決get、post請求方式下的亂碼問題
14、response對象的增強
Servlet API 中提供了response對象的Decorator設計模式的默認實現類HttpServletResponseWrapper , (HttpServletResponseWrapper類實現了response接口中的所有方法,但這些方法的內部實現都是僅僅調用了一下所包裝的的 response對象的對應方法)以避免用戶在對response對象進行增強時需要實現response接口中的所有方法。
15、response增強案例—壓縮響應
應用HttpServletResponseWrapper對象,壓縮響應正文內容。思路:
通過filter向目標頁面傳遞一個自定義的response對象。
在自定義的response對象中,重寫getOutputStream方法和getWriter方法,使目標資源調用此方法輸出頁面內容時,獲得的是我們自定義的ServletOutputStream對象。
在我們自定義的ServletOuputStream對象中,重寫write方法,使寫出的數據寫出到一個buffer中。
當頁面完成輸出後,在filter中就可得到頁面寫出的數據,從而我們可以調用GzipOuputStream對數據進行壓縮後再寫出給瀏覽器,以此完成響應正文件壓縮功能。
複習:Tomcat服務器內,提供對響應壓縮 配置實現
在conf/server.xml 中
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"/> 添加 compressableMimeType 和 compression
沒有壓縮 : 00:00:00.0000.0637553GET200text/htmlhttp://localhost/
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" compressableMimeType="text/html,text/xml,text/plain" compression="on"/>
壓縮後 : 00:00:00.0000.1712715GET200text/htmlhttp://localhost/
Content-Encoding: gzip
Content-Length : 2715
實際開發中,很多情況下,沒有權限配置server.xml ,無法通過tomcat配置開啓gzip 壓縮
編寫過濾器對響應數據進行gzip壓縮
flush 方法
只有沒有緩衝區字節流,FileOutputStream 不需要flush
而字節數組ByteArrayOutputStream、字節包裝流、字符流 需要flush ----- 這些流在調用close方法時都會自動flush