Cookie,Session Filter,Listener詳解

ihav2carryon發表於2024-08-22

HTTP請求的無狀態性

HTTP的無狀態性是其一個重要的特徵,指的是HTTP協議本身並不保留客戶端與伺服器互動的歷史資訊,換而言之,即每次的HTTP請求都是獨立的,伺服器在處理每一個請求時都不會記住前一個請求的狀態

無狀態的含義

  • 獨立性:每次的HTTP請求都是獨立的,不依賴於之前的請求,即伺服器處理每次請求時都會從頭開始,不會參照之前的狀態

E.g:假設A使用者在一個Web超市新增的一個商品到購物車中,等到再次購買時,伺服器已經無法分別判斷購買行為是屬於使用者A還是其他使用者

就是由於HTTP請求的無狀態性,伺服器無法識別其請求的上文狀態,因此人們就開發了新的技術來解決HTTP無狀態性帶來的狀態丟失問題,也就是我們接下來要講解的CookieSession 也借鑑了創造者@測試開發喵

Cookie

Cookie原意為”餅乾”,是由W3C組織提出的.目前的主流瀏覽器IE,Google,Edge,Firefox等等都支援了Cookie技術

Cookie的原理機制

鑑於HTTP請求協議是一個無狀態的請求協議,伺服器單從網路連線上無法得知客戶的身份.於是前輩們就想出了一個方法:給客戶端們都頒發一個通行證,這樣每次發起請求後都要攜帶自己的通行證.於是乎伺服器就可以從通行證上確認使用者的身份了

Cookie的本質

  • Cookie實際上是一小串文字資訊.當客戶端請求伺服器時,若伺服器要記錄該使用者的狀態,則使用response 向客戶端瀏覽器傳送一個Cookie.客戶端的瀏覽器會把Cookie儲存→當此瀏覽器再次申請伺服器時,瀏覽器會將請求的網站連同Cookie一起提交給伺服器→瀏覽器會檢查該Cookie,以此來辨別使用者的狀態,伺服器也可以根據需求修改Cookie中的內容

Cookie的型別

  • 持久型cookie
    • 以檔案方式存放在硬碟空間上的永久性cookie.持久cookie是值存放在客戶端硬碟中的cookie資訊(被設定了一段有效期)
      • 當使用者訪問網站時,瀏覽器會在本地硬碟上查詢與該網站相關聯的cookie.若該cookie存在,則瀏覽器會將其和頁面請求一起傳送到使用者所在的站點,之後伺服器會比對cookie中相應的屬性值與存放在伺服器中的資訊是否一致,以此判斷”新使用者”和老使用者
  • 會話型cookie
    • 停留在瀏覽器記憶體中的臨時cookie.會話型cookie.僅在會話期間存在,瀏覽器一旦關閉,會話型cookie就會被銷燬

Cookie中的屬性

  • Domain:指定Cookie會被哪些域名下的頁面訪問
  • Path:指定Cookie可以被哪些路徑下的頁面訪問
  • Secure:若設定為true,則只有在HTTPS連線下才會傳送Cookies,增加了安全性
  • HttpOnly:若設定為true,則Cookie不能被JavaScript訪問,有利於防止跨站指令碼攻擊
  • SameSite:設定Cookie是否隨著第三方請求一起傳送,有助於防止跨站偽造攻擊

JavaWeb對Cookie的操作

Java中將Cookie操作封裝到javax.servlet.http.Cookie類中,

Cookie物件的建立

  • new Cookie(String name, String value):設定Cookie的名稱與值
    • name:是Cookie的唯一標識,透過name可以區分不同的Cookie物件,一個網站可能有多個Cookie如username,language,等等
    • value:用於儲存實際的資料,透過cookie值可以跟蹤使用者狀態

Java中常用的Cookie的屬性

屬 性 名 描 述
String name 該Cookie的名稱。Cookie一旦建立,名稱便不可更改
String value 該Cookie的值。如果值為Unicode字元,需要為字元編碼。如果值為二進位制資料,則需要使用BASE64編碼
int maxAge 該Cookie失效的時間,單位秒。如果為正數,則該Cookie在maxAge秒之後失效。如果為負數,該Cookie為臨時Cookie,關閉瀏覽器即失效,瀏覽器也不會以任何形式儲存該Cookie。如果為0,表示刪除該Cookie。預設為–1
boolean secure 該Cookie是否僅被使用安全協議傳輸。安全協議。安全協議有HTTPS,SSL等,在網路上傳輸資料之前先將資料加密。預設為false
String path 該Cookie的使用路徑。如果設定為“/sessionWeb/”,則只有contextPath為“/sessionWeb”的程式可以訪問該Cookie。如果設定為“/”,則本域名下contextPath都可以訪問該Cookie。注意最後一個字元必須為“/”
String domain 可以訪問該Cookie的域名。如果設定為“.google.com”,則所有以“google.com”結尾的域名都可以訪問該Cookie。注意第一個字元必須為“.”
String comment 該Cookie的用處說明。瀏覽器顯示Cookie資訊的時候顯示該說明
int version 該Cookie使用的版本號。0表示遵循Netscape的Cookie規範,1表示遵循W3C的RFC 2109規範

Cookie的有效期

Cookie的maxAge決定著Cookie的有效期,單位為秒(second),Cookie中的getMaxAge()setMaxAge(int maxAge)可以用來讀寫maxAge屬性

  • maxAge為正數,則表示該Cookie會在maxAge秒後失效,瀏覽器會將maxAge為正數的Cookie持久化,即寫入到對應的Cookie檔案中.無論客戶關閉了瀏覽器還是電腦,只要在maxAge之內,訪問相應的網站仍然有效,
 Cookie cookie = new Cookie("username","張三");//新建cookie
        cookie.setMaxAge(Integer.MAX_VALUE);//設定生命時間為無限
        resp.addCookie(cookie);//響應到客戶端
					//此方法新增的cookie資訊永遠生效
  • maxAge為負數時則表示此cookie只在該瀏覽器視窗以及期子視窗生效,關閉瀏覽器即失效,maxAge為負數的Cookie為臨時Cookie,不會被持久化,即不會被寫入到檔案中,Cookie資訊只保留在瀏覽器的記憶體中,Cookie的預設值為-1,即預設為會話Cookie
  • maxAge為0時則表示,刪除該Cookie.Cookie的機制沒有提供刪除Cookie的方法,因此可以提供設定maxAge為0的方法即時失效,實現刪除Cookie的效果,失效的Cookie會被瀏覽器從檔案或記憶體中清除
  Cookie cookie = new Cookie("username","李四");//建立cookie
        cookie.setMaxAge(0);//設定為0,即時失效
        resp.addCookie(cookie);//相應到客戶端

Cookie的修改

  • Cookie本身並不提供修改的操作,若要修改一個Cookie,則需要新建一個同名的Cookie,新增到response中將原本的Cookie覆蓋
    • 注意點:修改時新建的Cookie除了value,maxAge之外的屬性,例如name,path,domian等等都要和原先的Cookie一致才能完成修改

Cookie的不可跨域名性

Cookie的不可跨域名性表示的是:Cookie通常只能由建立它的域名所訪問,這意味著一個域名下的Cookie不能被另一個域名下的Cookie訪問

  • 提供設定Domain屬性是實現不可跨域名性:

    • 例如當把Domian設定為:.example.com 時,則該Cookie不僅可以在example.com域名上被訪問,也可以在sub.example上被訪問
     @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            Cookie cookie = new Cookie("username","李四");//建立cookie
            cookie.setMaxAge(Integer.MAX_VALUE);
    
            cookie.setHttpOnly(true);//不可被JavaScript訪問
            cookie.setSecure(true);//設定只能提供Http傳送
            cookie.setDomain(".example.com");//cookie可以被example.com及其子域名訪問
            resp.addCookie(cookie);//相應到客戶端
        }
    

Session

Session也是一種解決HTTP請求無狀態性的技術,Session是伺服器使用的一種記錄客戶端狀態的機制,相比Cookie來說,Session更加簡單,但增加了伺服器的儲存壓力

Session的原理機制

Session是一種伺服器端的機制,伺服器使用一種雜湊表的結構儲存Session資訊

Session的行為

  • Session是依賴於Cookie的,使用者客戶端請求申請建立一個session→伺服器首先檢查客戶端請求中是否包含了一個session的標識—session id 若包含一個session id 則說明已經為該客戶端建立過session,伺服器會按照session id檢索出來直接使用,若檢索不到則新建一個;若不包含 session id,則為此客戶端新建一個session id並生成相應的session id→新建的session id會包含在cookie中隨響應一起返回到客戶端中儲存

  • 簡單來說:如果是cookie是檢查客戶端攜帶的”通行證”,來確認客戶.那麼session就是透過檢查伺服器上的”客戶端明細表”來確認客戶端身份,session相對於在伺服器上建立了一份客戶檔案.

Session的屬性

與Cookie一致的是 Session的值也是以key-value的形式存在的,Java中把將Session的操作封裝到javax.servlet.http.Session類中

獲取Session

  • 使用 request中的HttpSession getSession()方法可以獲取當前使用者的Session,若該使用者的Session不存在則返回null
    • 進而衍生出了另一種傳參的getSession()方法 HttpSession getSession(boolean create); 當create為true時,該方法會新建一個Session,再講Session返回
public class myServletTest extends HttpServlet {
	
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        HttpSession session1 = req.getSession(true);
    }
}

Session中常用的方法

方 法 名 描 述
void setAttribute(String attribute, Object value) 設定Session屬性。value引數可以為任何Java Object。通常為Java Bean。value資訊不宜過大
String getAttribute(String attribute) 返回Session屬性
Enumeration getAttributeNames() 返回Session中存在的屬性名
void removeAttribute(String attribute) 移除Session屬性
String getId() 返回Session的ID。該ID由伺服器自動建立,不會重複
long getCreationTime() 返回Session的建立日期。返回型別為long,常被轉化為Date型別,例如:Date createTime = new Date(session.get CreationTime())
long getLastAccessedTime() 返回Session的最後活躍時間。返回型別為long
int getMaxInactiveInterval() 返回Session的超時時間。單位為秒。超過該時間沒有訪問,伺服器認為該Session失效
void setMaxInactiveInterval(int second) 設定Session的超時時間。單位為秒
void putValue(String attribute, Object value) 不推薦的方法。已經被setAttribute(String attribute, Object Value)替代
Object getValue(String attribute) 不被推薦的方法。已經被getAttribute(String attr)替代
boolean isNew() 返回該Session是否是新建立的
void invalidate() 使該Session失效

Session與Cookie的關係

  • Session實現的原理與Cookie有關

    • 當伺服器建立了Session物件後,首先將其存入伺服器記憶體,並把Session的唯一標識session id以Cookie的形式寫回客戶端本地檔案中(鍵名為JSESSIONID,值為該session的id)

       protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      
              HttpSession session = req.getSession();
              Cookie cookie = new Cookie("JSESSIONID",session.getId());
              cookie.setMaxAge(60*60);
              resp.addCookie(cookie);
          }
      
  • Session的儲存結構

    Session是以Map資料結構儲存的key值為session id;value值為Session物件

    protected Map<String, Session> sessions = new ConcurrentHashMap<>();
    
  • Session與Cookie的區別

    • 安全性:
      • Session:儲存在伺服器中,更加安全
      • Cookie:儲存在客戶端,更容易受到攻擊,可以設定HttpOnly,和Secure屬性來提高安全性
    • 資料量:
      • Session:可以儲存較大的資料量,沒有嚴格的大小限制
      • Cookie:提出限制在4kb左右
    • 應用場景:
      • Session:用於跟蹤使用者狀態,適合儲存敏感資訊,如身份驗證等等
      • Cookie:用於儲存非敏感資訊,適合短期儲存資料

Filter過濾器

Filter過濾器是Servlet技術中最實用的部分,Web開發人員透過Filter技術管理Web伺服器的所有資源,如JSP,Servlet,HTML等等,主要用於對客戶請求進行預處理

Filter的主要功能

  • 編碼轉換:在請求資源到達資源之前轉換字元編碼,確保字元編碼的一致性
  • 日誌記錄:在請求和響應中記錄相關資訊
  • 預處理和後處理認任務:
    • 修改請求頭或響應頭
    • 新增快取控制頭
    • 進行安全檢查

Filter的配置

Filter有兩種配置方法,一種是在web.xml檔案中配置,第二種是使用註釋的方法配置(Java Servlet 3.0 及以上版本)

web.xml配置

借鑑作者@coderland

 <filter>
       <filter-name>myFilter01</filter-name>//設定filter名稱
       <filter-class>com.mashang.web.myFilter01</filter-class>
   //指定用於指定過濾器的完整類路徑
   </filter>

    <filter-mapping>//設定Filter負責攔截的資源
        <filter-name>myFilter01</filter-name>//需和filter-name一致
        <url-pattern>/ *</url-pattern>//設定filter攔截的路徑 URL結構
    </filter-mapping>

註釋配置

@WebFilter()

  • Filter實現類,頂部使用@WebFilter()進行註釋
  • @WebFilter()中的屬性
    • String filterName() default "";:指定Filter名稱
    • String[] urlPatterns() default {};:指定Filter攔截的URL,使用/*表示所有URL都會經過此Filter
      • 由於urlPatterns以字串陣列存在,因此可以一次配置多條路徑,例如

        @WebFilter(filterName = "MyFilter", urlPatterns = {"/secure/*", "/admin/*"})
        

@WebInitParam()

@WebInitParam註釋可用於在@WebFilter中配置引數初始值

  • WebInitParam[] initParams() default {};:是@WebFilter中的引數類別,預設為空值,表示沒有被初始化
    • 其有兩個引數:name:引數名稱;value:引數值
@WebFilter(filterName = "myFilter01",urlPatterns = "/*",
        initParams ={
        @WebInitParam(name = "encoding",value = "UTF-8")
       //將其中的encoding 屬性配置為UTF-8,來實現編碼統一
        }
)
public class myFilter01 implements Filter { }

Filter的生命週期

Filter是Java的一個介面,其有一個簡單的生命週期,

  • void init(FilterConfig config)
    • 與Servlet程式一致,Filter的建立和銷燬都是由Web伺服器操作的,Web伺服器啟動時建立Filter物件,並呼叫init()方法完成初始化工作,讀取web.xml檔案,Filter物件只會建立一次,因此init()方法只會執行一次,
    • FilterConfig 是 Java Servlet API 中的一個介面,它提供了獲取 Filter 配置資訊的方法,在下文我們會展開講解
  • **void doFilter(ServletRequest serReq, ServletResponse** **serResp, FilterChain filterChain)**
    • 完成實際過濾操作的方法.Web伺服器會在每次屌用service()方法之前呼叫Filter的doFilter()方法
    • FilterChain:Filter鏈,在開發中會編寫多個Filter,FilterChain是Filter的集合,如果呼叫了FilterChain物件的doFilter方法,則web伺服器會檢查FilterChain物件中是否還有filter,如果有,則呼叫第2個filter,如果沒有,則呼叫目標資源
  • void destroy()
    • Filter物件建立後會駐留在Web記憶體中,當Web用於結束伺服器停止時,Web伺服器銷燬之前的物件
    • 與init()一致的是destroy()也只呼叫一次

FilterConfig介面

當Filter被初始化時,FilterConfig物件會被傳遞給init()方法,在init()中透過FilterConfig,可以回去Filter的名稱,初始化引數,及相關的ServletContext

FilterConfig主要方法

  • String getFilterName(): 返回Filter的名稱

  • String getIntiParameter(String name):獲取指定名稱的初始引數的值,若沒找到對應的初始化引數則返回null

  • Enumeration<String> getInitParameterNames():返回所有初始化引數名稱,

  • ServletContext getServletContext():返回與Filter關聯的ServletContext物件,ServletContext物件提供了整個Web應用程式的訪問

       public void init(FilterConfig filterConfig) throws ServletException {
            //獲取FilterConfig中的所有引數名
            Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
            while (initParameterNames.hasMoreElements())
            {
                String name = initParameterNames.nextElement();
                System.out.println("param"+":"+name);
            }
            ServletContext servletContext = filterConfig.getServletContext();
            servletContext.setAttribute("name","value");
        }
    

用Filter實現編碼統一

@WebFilter(urlPatterns = "/*")
public class myFilter01 implements Filter {
    private String characterEncoding=null;
    @Override
    public void init(FilterConfig fC) throws ServletException {
        //在初始定義中獲取characterEncoding
        //若為非空,encoding也是非空 則直接獲取 encoding的值
        if (fC != null && fC.getInitParameter("encoding") !=null
        && ! fC.getInitParameter("encoding").equals(""))
        {
            characterEncoding=fC.getInitParameter("encoding");
        }
        else{
            characterEncoding="UTF-8";//否則置為utf8
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //將Servlet轉換為HttpServlet用於處理HTTP請求響應
        HttpServletRequest req  = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        //攔截所有請求進行統一編號
        //指定request和response的編號
        req.setCharacterEncoding(characterEncoding);
        //將response資料響應為utf8
        resp.setCharacterEncoding(characterEncoding);
        //告訴瀏覽器輸出內容為HTML格式,
        resp.setContentType("text/html;charset="+characterEncoding);
        filterChain.doFilter(req,resp);//將請求和響應傳遞給下一個 Filter 或目標資源
    }

Listener

Listener 監聽器與Servlet 程式,Filter 過濾器共稱JavaWeb的三大元件,Listener作用application,session,request三個物件中,Listener用於監聽特點的時事件,然後回撥函式,並做出相應的反應,Listner主要分為三個大類:ServletContext 監聽,Session監聽,Request 監聽;Listener本質是個介面

Listener的分類及使用

ServletContext監聽

ServletContext監聽是由ServletContextListenerServletContextAttributeListener介面實現的,

ServletContextListener方法

對整個Servlet上下文進行監聽(建立或銷燬)

  • public void contextInitialized(ServletContextEvent sce);:執行初始化任務,如載入快取等等
  • public void contextDestroyed(ServletContextEvent sce);:停止時執行清除任務,釋放資源,關閉連線等等
  • ServletContextEvent物件的操作:
    • public ServletContext getServletContext();:取得一個ServletContext(application)物件

ServletContextAttributeListener方法

對Servlet上下文屬性的監聽(增刪改屬性)

  • public void attributeAdded(ServletContextAttributeEvent scab);:向ServletContext中新增屬性
  • public void attributeRemoved(ServletContextAttributeEvent scab);:刪除ServletContext中的屬性
  • public void attributeRepalced(ServletContextAttributeEvent scab);:替換SevletContext中的屬性,(重複設定無效果)
  • ServletContextAttributeEvent物件的操作:能獲取屬性名於值
    • public String getName();:獲取屬性名
    • public Object getValue();:獲取屬性的值

Session監聽

ServletContext類似,Session也分為兩種介面,分別為HttpSessionListenerHttpSessionAttributeListener

HttpSessionListener方法

對Session整體狀態的監聽

  • public void sessionCreated(HttpSessionEvent se);:建立Session
  • public void sessionDestroyed(HttpSessionEvent se);:銷燬Session
  • HttpSessionEvent物件的操作:
    • public HttpSession getSession();:取得當前操作的session

HttpSessionAttributeListener方法

對Session屬性的監聽

  • public void attributeAdded(HttpSessionBindingEvent se);:新增屬性到HttpSession
  • public void attributeRemoved(HttpSessionBindingEvent se);:將一個屬性從HttpSession中刪除
  • public void attributeReplaced(HttpSessionBindingEvent se);:替換HttpSession中的屬性
  • HttpSessionBindingEvent物件的操作
    • public String getName();:取得屬性的名
    • public Object getValue();:取得屬性的值
    • public HttpSession getSession();:取得當前的session

Request監聽

Request監聽分為ServletRequestListenerServletRequestAttributeListener

ServletRequestListener方法

  • public void requestInitialized(ServletRequestEvent sre);:request初始化
  • public void requestDestroyed(ServletRequestEvent sre);:request銷燬
  • ServletRequestEvent物件的操作
    • public ServletRequest getServletRequest();:取得一個ServletRequest物件
    • public ServletContext getServletContext();:取得一個ServletContext(application)物件

ServletRequestAttributeListener方法

  • public void attributeAdded(ServletRequestAttributeEvent srae);:往當前ServletRequest物件中增加屬性
  • public void attributeRemoved(ServletRequestAttributeEvent srae);往當前ServletRequest物件中刪除屬性
  • public void attributeReplaced(ServletRequestAttributeEvent srae);屬性替換(第二次設定同一屬性)
  • ServletRequestAttributeEvent物件操作
    • public String getName();:得到屬性名稱
    • public Object getValue();:取得屬性的值

Listener的配置

  • web.xm配置

    原理於Servlet,Filter大同小異,不展開講解

    <listener>
        <listener-class>com.listener.class</listener-class>
    </listener>
    
  • 註釋@WebListener

    • 往Listener實現類頂部加上@WebListener註釋即可
    @WebListener
    public class myListener01 implements HttpSessionAttributeListener {}
    

Listener的例項應用

使用HttpSessionListener統計最大線上人數

@WebListener
public class myListener01 implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent event) {
        //使用Session獲取ServletContext物件
        ServletContext app = event.getSession().getServletContext();
        //獲取Servlet中的count屬性
        int count = (int) app.getAttribute("onLineCount");
        count++;//每次建立一個Session Count就會+1 用來計數
        app.setAttribute("onLineCount",count);
        int maxOnLineCount= (int) app.getAttribute("maxOnLineCount");
        //若count>maxOnLineCount 則將maxOnLineCount更新為count的值
        if (count>maxOnLineCount)
        {
            app.setAttribute("maxOnLineCount",count);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            app.setAttribute("date",sdf.format(new Date()));
        }
    }
         
    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        //結束會話後會刪除Session 即減少一位客戶 count--
        ServletContext app = event.getSession().getServletContext();
        int count = (int)app.getAttribute("count");
        count--;
        app.setAttribute("count", count);
    }
}

相關文章