javaweb教程 servlet第二部分

Cookie

cookie基本概念

概念
cookie是保存在瀏覽器客戶端的簡單的文本文件,用於存儲web服務器下發的簡單數據。不超過4K大小,每個應用不超過200條cookie鍵值對。瀏覽器每次訪問web應用,都會把該應用對應的cookie記錄全部帶上。如果是自己實現的http客戶端,需要自己去實現。

場景
cookie相當於理髮店的會員卡ID,客戶每次去理髮店,給前臺人員報出會員卡ID,後臺人員根據這個會員卡ID,去櫃子裏面查找對應的會員卡信息,比如櫃子某一頁記錄頁對應這個會員卡ID,記錄頁面上記錄了辦卡人姓名,年齡,充值了多少錢等。

組成

  • key-value :
    設置Cookie的名稱和值,用於http請求時,提交給服務器調端該用戶的信息。
  • Expires :
    設置Cookie的生存事件。Cookie分爲持久型和會話型,如果不設置expires,則cookie爲會話型,存儲於內存之中,瀏覽器關閉則消失;如果將expires設置爲負數,則表示瀏覽器端收到cookie之後,直接丟棄;如果將expires設置爲0,則表示瀏覽器端如果存儲有該cookie,刪除原有cookie,當然新的也不會進行存儲;如果expires設置爲正數,則cookie會持久性,瀏覽器關閉的時候,不會消失,時間到了就自動刪除。
  • Path :
    定義web站點上可以訪問該cookie的目錄,用於不同的訪問path,傳遞不同的cookie,減少cookie的攜帶量。
  • Domian :
    指定可以訪問該cookie的web站點或者域。允許一個子域設置或者訪問父域的cookie。(比如單點登錄的cookie共享)
  • Secure :
    指定是否使用https協議發送cookie。
  • HttpOnly :
    用於防止客戶端js通過Document.cookie屬性訪問cookie,但是部分瀏覽器未遵守此規定。且大多數瀏覽器允許通過XMLHTTP對象讀取HTTP響應頭中的Set-Cookie頭。



HTTP請求與響應中cookie存在的形式

  • HTTP請求的cookie的形式。一個’cookie’字段。
    在這裏插入圖片描述

  • HTTP響應中的cookie的形式。多個’Set-Cookie’字段。
    在這裏插入圖片描述



cookie的前後端使用

cookie的後端使用

  • 創建cookie,並且設置到響應頭傳給瀏覽器
#每一個key-value,都對應一個Cookie實例。
#創建cookie,並設置key-value
Cookie cookie = new Cookie("username", "meng"); 
//設置過期時間Expires
response.setMaxAge(10000);
//設置path
response.setPath("/test");
//添加到響應頭,http發送響應請求的時候會返回給瀏覽器
response.addCookie(cookie);

Cookie cookie2 = new Cookie("password", "123456");
response.addCookie(cookie2);
  • 獲取cookie(瀏覽器每次http請求默認攜帶該web所有的cookie)
    請求體HttpServletRequest只有cookie的key-value,沒有其他屬性,如path,expires等
Cookie[] cookieList = request.getCookies();
for( Cookie cookie : cookieList ){
	System.out.println(  key + " : "+ value);
}
  • 修改cookie
    如果要修改瀏覽器中存儲的cookie,只需要發送同名key就行,瀏覽器會保證key的唯一,用新value覆蓋就value。
#瀏覽器接收到已經存儲的key-value, 則直接覆蓋。
#對於服務器而言,修改瀏覽器的cookie操作與新增一樣。
Cookie cookie2 = new Cookie("password", "1234567890");
response.addCookie(cookie2);
  • 刪除cookie
    當瀏覽器接收到expires爲0的cookie時,會根據key去查找已存儲的cookie,如果存在,則刪除。當然expires爲0的cookie也不會被保存。
Cookie cookie2 = new Cookie("password", "1234567890");
cookie2.setMaxAge(0);
response.addCookie(cookie2);

cookie不能使用中文以及加密處理

cookie不能存儲中文問題處理

  • 編碼存儲cookie中的中文
Cookie cookie = new Cookie("password3", URLEncoder.encode("這就是中文", "UTF-8"));
cookie.setPath("/test");
cookie.setMaxAge(10000);
cookie.setHttpOnly(true);
resp.addCookie(cookie);
  • 編碼獲取cookie中的中文
Cookie[] cookies =  req.getCookies();
if ( cookies != null ){
for ( Cookie cookie : cookies ){
      System.out.println(cookie.getName() +" : "+ cookie.getPath() + " : "+ URLDecoder.decode(cookie.getValue(),"utf-8"));
   }
}

cookie對於重要內容的加密處理

以後實際項目中有使用再添加。。。


cookie的前端使用

判斷cookie是否開啓

#BOM navigator對象提供的屬性
if ( !navigator.cookieEnabled ){
     console.log("cookie被禁止");
}

關於使用cookie進行存取來判斷cookie是否開啓,容易誤判

  • cookie的httpOnly屬性,用於告訴瀏覽器cookie不允許被js代碼存取,但是有部分瀏覽器不允許讀,但是允許寫。所以導致cookie雖然開啓可用,但是通過cookie進行存取判斷cookie是沒有開啓的。

獲取cookie

  • document.cookie是獲取所有cookie鍵值對。
  • #document.cookie只能獲取到path爲"/“下的cookie,其他的獲取不到。測試過程中,有兩條cookie,一條沒設置path(“username”:“meng”),一條設置path爲”/test" (”password“,:“123456”), 雖然我訪問的網頁爲 http://localhost:8080/test/index.html,但是js獲取的cookie只顯示(“username”:“mengze”),沒有(“password”:“123456”)。但是在請求該網頁的請求頭中看到了這兩條cookie。
var cookie = document.cookie;
console.log(cookie)

修改cookie

  • document.cookie=xxx 是以追加的形式添加cookie,如果不是同名key,那麼不會覆蓋之前的cookie內容,而是追加。當然,如果是同名key,那麼就是覆蓋了。
console.log(document.cookie);
document.cookie = "username1=meng123;expires=30000;path=/";
console.log(document.cookie);

增加cookie

  • 增加與修改類似,同名key就直接覆蓋,不同名的key就追加。
document.cookie = "username1=meng123;expires=30000;path=/";

刪除cookie

  • 使用chrome測試cookie刪除,無效。

XMLHttpRequest在setRequestHeader設置cookie無效

request.setRequestHeader("cookie", "name: 111");


關於cookie相關的安全問題與防禦





HttpSession

什麼是session?

如果只是用cookie來證明用戶身份,那麼cookie需要存儲大量的信息,且這些信息還不一定安全可靠。Session是另一種記錄用戶狀態的機制。不同於cookie保存在客戶端瀏覽器中,而是保存在服務器中。通過在瀏覽器中保存一個cookie(“JSESSIONID”,“xxxxxx”)來作爲一個用戶標識,然後在服務器中獲取SESSIONID對應的Session,然後從session拿去之前存儲的用戶信息。




創建session

servlet中創建session

#調用無參的或者參數爲truegetSession(),如果當前用戶對應的session不存在,那麼就創建一個新session。如果存在session,就返回原有的session。可以通過session.isNew()判斷session是新建的,還是舊的。

#調用參數爲falsegetSession(),如果當前用戶對應的session不存在,那麼就返回false。如果存在session,就返回原有的session。

httpServletRequest.getSession(); //相當於參數爲true。
httpservletRequest.getSession(Boolean);

jsp中創建session

  • jsp頁面默認開啓session,也就是說只要訪問了該頁面,就會創建session。




會話控制流程

  • 用戶訪問服務器時 未攜帶JSESSIONID="xxxxx"的cookie , 此時如果存在getSession(),或者getSession(true), 或者訪問的jsp頁面開啓了session。那麼就會創建對應該用戶的httpSession,並且將sessionID通過cookie形式發送給瀏覽器,並且以 JSESSIONID="xxxx"形式存在。
  • 用戶訪問服務器時 攜帶JSESSIONID="xxxxx"的cookie, 那麼服務器如果能找到session(當然如果session有效期過了,那麼也需要重新創建session),則直接返回存在的session。在JESSIONID-Session能對應的情形下,永遠是同一個session對象。
#
HttpSession session = req.getSession(false);
if ( session == null ){
     //session沒有創建,創建session
}else {
     //session存在,可以直接使用
}
//一般是在登錄的時候創建httpSession
HttpSession session =  req.getSession();
if ( session.isNew() ){
    //表明session是新建的,並沒有存儲任何數據。
}else {
    //表明session是舊的。
}




管理session有效時間

獲取session的有效時間

//查看session的有效時間, 以秒鐘爲單位
long  timeout =  session.getMaxInactiveInterval();
System.out.println(timeout);

tomcat中session默認的有效時間與修改

#tomcat默認時間爲1800秒,可以去tomcat中的conf目錄的we'b.xml中看到。
#也可以在自己項目中的we'b.xml中配置
<session-config>
    <session-timeout>30</session-timeout>  #這個時間單位是分鐘。
</session-config>

代碼形式修改session的有效時間

//設置session的有效時間
session.setMaxInactiveInterval(10);

強制session失效(一般用於登出)

  • session.invalidate()函數,會清空當前session中所有存儲的對象,並且會將該session設置爲無效。下次有該session對應的JESSIONID傳過來時,不會再獲取到該session。
session.invalidate();




關於瀏覽器禁用cookie之後的處理 --> url重寫

如果cookie不可存取,那麼瀏覽器就不會在http請求時自動攜帶jsessionid。這個時候,可以在URL上攜帶jsessionid。一樣能被tomcat解析到。

原理:

  • 針對前後端分離的網站或者APP,只要將sessionID存儲在本地,然後手動設置請求頭的cookie就行。對於網頁的ajax設置cookie失效的情形,採用在URL上攜帶( ;jsessionid=xxxxxxx )就行。
#在URL上攜帶( ;jsessionid=xxxxxxx ), 也能讓tomcat獲取到jessionid,並且獲取到對應的session。

http://localhost:8081/test/hello;jsessionid=DEF0234640EA834D79038F6FE66E8A0C

URL重定向處理

#非重定向的url處理(一般是動態網頁處理<a>, <form>等上面的請求URL)
String uri = "otherServlet";
uri = response.encodeURL(uri);
out.println("<a href='" + uri + "'>跳轉</a>到otherServlet");
#重定向的URL處理
String url = request.getCotnextPaht() + "/otherServlet";
uri = response.encodeRedirectURL(uri);
response.sendfRedirect(uri);



關於JSESSIONID,HttpSession的創建時機

JSESSIONID就是tomcat獲取用戶對應HttpSession的cookie。

HttpSession創建時間

  • tomcat中HttpSession是在 servlet中調用 httpServletRequest.getSession() 或者 httpServletRequest.getSession(true), 或者JSP中配置了開啓session時創建。
  • 有些網站在用戶還沒登錄就創建很多HttpSession的原因,一般就是JSP頁面默認開啓的session,導致用戶還未登錄就創建了session。
  • 當然,這裏要說的就是,服務器不是在啓動時就創建HttpSession的,而是在需要的時候(比如調用getSession方法)。
  • 另外,session對應的JSESSIONID也不是第一次訪問web站點就會返回給瀏覽器的。是在httpSession創建之後的那次服務器響應返回後,帶給瀏覽器的。

JSESSIONID創建時間

  • 瀏覽器cookie中存的JSESSIONID,並不是第一次訪問網頁的時候就返回並存儲的。而是當服務器當爲該用戶創建了對應的httpSession實例的時候,響應頭中’Set-Cookie’中攜帶的。(也就是說會員卡並不是你第一次去店裏就創建的,而是當你願意辦理的時候才辦理的,然後在辦理了會員卡之後,下次再來的時候,你纔有卡號可以出示,以及能查到你對應的會員卡以及詳細信息。)




關閉服務器保持session

鈍化與活化

  • 現象
    服務器關閉後重新啓動,只要瀏覽器沒關,還是能獲取session裏面的內容。

  • 鈍化
    服務器關閉以後,會將session對象序列化保存在磁盤中。可以在tomcat的work/當前項目下觀察到SESSION.SER文件。

  • 活化
    服務器再次啓動時,把之前的序列化號的文件加載進來,就會再次加載之前保存的session。(SESSION.SER包含了session域中所有內容)。當然, 如果session存儲的對象沒有實現序列化接口,將不會被序列化保存。




關閉瀏覽器保持session

  • session創建時,默認30分鐘有效時間。同時,返回的sessionid沒有設置expires,也就說,是會話型cookie,瀏覽器關閉該jsessionid就會消失。
  • 可以採用以下方式來創建持久性的關於JSESSIONID的cookie。
#手動獲取sessionid,然後創建cookie('jsessionid', sessionid),然後設置expires,這樣瀏覽器中的JESSIONID內容的cookie就是持久性的了。

HttpSession session = request.getSession();
String id = session.getId();
Cookie cookie = new Cookie('jsessionid', id);
cookie.setMaxAge(60*60*24);
response.addCookie(cookie);




session實例–用戶的登入登出

#登錄 -- 將用戶信息存放到session中
resp.setContentType("text/json;charset=utf-8");
//登錄處理
String username = req.getParameter("username");
String password = req.getParameter("password");
if( "meng".equals(username) && password.equals("123456")){
    //賬號密碼正確,則登錄成功
     HttpSession session = req.getSession();
     session.setAttribute("username", username);
     resp.getWriter().write("登錄成功");

}else {
    resp.getWriter().write("登錄失敗");
}


#登出 -- 將用戶信息移除session,且作廢該session。
resp.setContentType("text/json;charset=utf-8");
HttpSession session =  req.getSession(false);
if ( session != null ){
    session.invalidate();
}
resp.getWriter().write("退出成功");

Filter

Filter的基本概念

Filter是web服務器的三大組件(Servlet, Filter, Listener)之一.用於用戶請求在到達servlet之前, 以及服務器響應到達瀏覽器之前。進行請求攔截或者功能增強,或者響應增強。

Filter的使用

web.xml中配置Filter

<filter>
    <filter-name>filterB</filter-name>
    <filter-class>com.mz.BFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>filterB</filter-name>
    <url-pattern>/hello</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

實現Filter接口

public class HelloFilter  implements Filter {

    private FilterConfig filterConfig;

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

       String uri =  ((HttpServletRequest) servletRequest).getRequestURI();
       if ( uri.endsWith(".jsp") ){
           System.out.println("攔截此次請求");
       }else {
           filterChain.doFilter(servletRequest, servletResponse);
       }

    }

    public void destroy() {

    }
}

Filter的執行流程/原理

在執行Servlet的前後,進行了請求攔截,就響應攔截處理。
在這裏插入圖片描述
作用:

  • Filter可以在請求到達目標資源(JSP、Servlet、Html、CSS)之前攔截請求,並作相應處理。
  • Filter可以放行請求,使請求到達目標資源。
  • Filter可以在響應到達瀏覽器之前做一些處理。

Filter的生命週期

單例多線程形式存在。

  • Servlet容器啓動,Filter就創建並且初始化。在容器中以單例多線程形式存在。
  • 每次用戶請求都執行doFilter方法。
  • Servlet容器停止後,Filter銷燬。

urlpattern的配置

1、精確匹配

  • 要攔截的資源的詳細路徑。需要請求路徑的字符串完全相等。比如/a.jsp; /b/c.jsp;

2、路徑匹配

  • 攔截指定路徑下的所有請求。 比如 /a/*
    /* 表示攔截所有請求。
    /path/* 表示攔截/path路徑下的所有請求。
    

3、後綴匹配

  • 攔截所有以xxx後綴結尾的所有請求。
     *.jsp
    

4、錯誤舉例 /page/*.jsp

FilterConfig對象

Filter對象在創建並且初始化時,會調用init(FilterConfig):void方法,並且將該對象傳遞給Filter對象。 FilterConfig含有web.xml中中配置的初始化參數。以及ServletContext對象。

#web.xml中配置初始化參數
<filter>
    <filter-name>filterB</filter-name>
    <filter-class>com.mz.BFilter</filter-class>
    <init-param>
        <param-name>name</param-name>
        <param-value>meng</param-value>
    </init-param>
    <init-param>
        <param-name>age</param-name>
        <param-value>18</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>filterB</filter-name>
    <url-pattern>/hello</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>
#在FilterB中獲取配置的初始化參數,以及ServletContext
public class BFilter implements Filter {

    private FilterConfig filterConfig;

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        ServletContext servletContext = filterConfig.getServletContext();
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        String name = this.filterConfig.getInitParameter("name");
        String age  = this.filterConfig.getInitParameter("age");

        filterChain.doFilter(servletRequest, servletResponse);
    }

    public void destroy() {

    }
}

Filter 根據request參數攔截部分請求

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

   String uri =  ((HttpServletRequest) servletRequest).getRequestURI();
   if ( uri.endsWith(".jsp") ){
       System.out.println("攔截此次請求");
       return;
   }
   filterChain.doFilter(servletRequest, servletResponse);
}

Filter中的<dispatcher>

Filter中<dispatcher>有四個取值. 一個Filter可以配置多個<dispatcher>.

  • REQUEST :默認值,表示攔截用戶的直接請求。
  • FORWARD :表示請求轉發。
  • INCLUDE : 表示攔截包含頁面。
  • ERROR :表示攔截web.xml中配置的錯誤頁面。
<filter-mapping>
    <filter-name>filterB</filter-name>
    <url-pattern>/hello</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

Filter攔截器鏈

對於同一個用戶請求,可以配置多個Filter攔截器,從而形成攔截器鏈。用戶請求在攔截器鏈串行執行,處理的順序是web.xml中對應的Filter的配置順序;服務器響應在攔截器鏈中處理的順序則相反。比如配置了Filter A、B、C,那麼用戶請求來時,A->B->C, 服務器響應時,C->B->A。
在這裏插入圖片描述


Filter實例 --> 權限驗證

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest) servletRequest;
    HttpServletResponse resp = (HttpServletResponse) servletResponse;

    servletResponse.setContentType("text/json;charset=utf-8");
    //獲取session,如果是存在該用戶,就表示已經登錄
    //如果不存在session,那麼就沒有登錄
    HttpSession httpSession = req.getSession(false);
    if (httpSession == null){
        resp.getWriter().write("用戶沒有登錄");
        return;
    }else {

         filterChain.doFilter(servletRequest, servletResponse);
    }
}



Listener

Listener的基本概念

概念

  • listener也就是監聽器,用於監聽一個特定的對象,當被監聽的對象變化時,監聽的對象會執行一系列動作。

作用

  • 用於三大域對象的創建銷燬監聽
ServletContextListener  #監聽ServletContext的創建與銷燬  
HttpSessionListener     #監聽Session的創建與失效
ServletRequestListener  #監聽ServletRequest的創建於銷燬
  • 三大域對象對屬性增加、修改、刪除監聽;
ServletContextAttributeListener #監聽ServletContext增加,刪除,修改屬性
HttpSessionAttributeListener  #監聽HttpSession增加,刪除,修改屬性
ServletRequestAttributeListener #監聽ServletRequest增加,刪除,修改屬性
  • 對某個屬性在session中的增加,刪除監聽。
HttpSessionActivationListener   #監聽對象在session中的鈍化與活化
HttpSessionBindingListener      #監聽對象被添加與刪除

生命週期監聽器

ServletContextListener
用於監聽servletContext容器對象的創建與銷燬。用於在用戶訪問之前,創建或者初始化必要的支持對象。比如spring容器對象的創建。

public class MyServletContextListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("web容器創建");
    }

    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("web容器銷燬");
    }
}

HttpSessionListener
用於監聽HttpSession對象的創建與失效。 一個HttpSession代表一個用戶,所以可以監聽用戶數量,會員數量,訪問量,訪問時間等。

  • 關於httpSession的生命週期: httpSession是在調用getSession(),且參數不爲false的情形下創建的;httpSession是在有效時間過期,或者強制失效的情況下銷燬的。與web服務器的開啓關閉沒關係。(即使關閉,session也會被鈍化,而不是被刪除)
public class MySessionListener implements HttpSessionListener {
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println("有新的session創建");
    }

    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("有session銷燬");
    }
}

ServletRequestListener
用於監聽ServletRequest(HttpServletRequest)的創建與監聽。(請求轉發過程中,一直是同一個ServletRequest)

public class MyServletRequestListener implements ServletRequestListener {


    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {

    }

    public void requestInitialized(ServletRequestEvent servletRequestEvent) {

    }
}

屬性監聽器

ServletContextAttributeListener
監聽ServletContext的域中屬性的添加,刪除,修改。

public class MyServletContextAttributeListener implements ServletContextAttributeListener {


   public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {

   }

   public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {

   }

   public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {

   }
}

HttpSessionAttributeListener
用於監聽HttpSession域對象中屬性的添加,刪除,修改。

public class MySessionAttributeListener implements HttpSessionAttributeListener {
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {

    }

    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {

    }

    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {

    }
}

ServletRequestAttributeListener
用於監聽ServletRequest(HttpServletRequest)域中的屬性增加,刪除,修改。

public class MyServletRequestAttributeListener implements ServletRequestAttributeListener {
    public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {

    }

    public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {

    }

    public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {

    }
}

session固有監聽器

HttpSessionActivationListener 用於添加到session中的屬性監聽,如果鈍化,激活事件發生,則會調用該屬性實現的ActivationListener接口的方法。

public class UserInfo implements Serializable, HttpSessionActivationListener {

    public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
        System.out.println("session中的userinfo即將鈍化");
    }

    public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
        System.out.println("session中的userinfo已經激活");
    }
}


HttpSessionBindingListener 監聽session中某個屬性的增加,刪除。

public class UserInfo implements Serializable, HttpSessionBindingListener {


    public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("該屬性被添加到了session中");
    }

    public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("該屬性從session中移除");
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章