servlet第二部分
- Cookie
- HttpSession
- 什麼是session?
- 創建session
- 會話控制流程
- 管理session有效時間
- 關於瀏覽器禁用cookie之後的處理 --> url重寫
- 關於JSESSIONID,HttpSession的創建時機
- 關閉服務器保持session
- 關閉瀏覽器保持session
- session實例--用戶的登入登出
- Filter
- Filter的基本概念
- Filter的使用
- Filter的執行流程/原理
- Filter的生命週期
- urlpattern的配置
- FilterConfig對象
- Filter 根據request參數攔截部分請求
- Filter中的``
- Filter攔截器鏈
- Filter實例 --> 權限驗證
- Listener
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
#調用無參的或者參數爲true的getSession(),如果當前用戶對應的session不存在,那麼就創建一個新session。如果存在session,就返回原有的session。可以通過session.isNew()判斷session是新建的,還是舊的。
#調用參數爲false的getSession(),如果當前用戶對應的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中移除");
}
}