Cookie簡介
Cookie意爲「甜餅」,是由W3C組織提出,最先由Netscape社區發展的一種機制。 目前Cookie已經成爲標準,全部的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。html
因爲HTTP是一種無狀態的協議,服務器單從網絡鏈接上無從知道客戶身份。 怎麼辦呢?就給客戶端們頒發一個通行證吧,每人一個,不管誰訪問都必須攜帶本身通行證。 這樣服務器就能從通行證上確認客戶身份了。這就是Cookie的工做原理。web
Cookie其實是一小段的文本信息。 客戶端請求服務器,若是服務器須要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。 客戶端瀏覽器會把Cookie保存起來。 當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。 服務器檢查該Cookie,以此來辨認用戶狀態。 服務器還能夠根據須要修改Cookie的內容。數據庫
Cookie機制
Cookie技術是客戶端的解決方案,Cookie就是由服務器發給客戶端的特殊信息,而這些信息以文本文件的方式存放在客戶端, 而後客戶端每次向服務器發送請求的時候都會帶上這些特殊的信息。跨域
具體過程以下:瀏覽器
用戶使用瀏覽器訪問一個支持Cookie的網站的時候,用戶會提供包括用戶名在內的我的信息而且提交至服務器;
服務器在向客戶端回傳相應的超文本的同時也會發回這些我的信息,固然這些信息並非存放在HTTP響應體 (Response Body)中的,而是存放於HTTP響應頭(Response Header)
客戶端瀏覽器接收到來自服務器的響應以後,瀏覽器會將這些信息存放在一個統一的位置。 對於Windows操做系統而言,咱們能夠從: [系統盤]:Documents and Settings[用戶名]Cookies目錄中找到存儲的Cookie;
客戶端再次向服務器發送請求的時候,都會把相應的Cookie再次發回至服務器。 而此次,Cookie信息則存放在HTTP請求頭(equest Header)了。
HTTP的Cookie機制
Web應用程序是使用HTTP協議傳輸數據的。HTTP協議是無狀態的協議。 一旦數據交換完畢,客戶端與服務器端的鏈接就會關閉,再次交換數據須要創建新的鏈接。 這就意味着服務器沒法從鏈接上跟蹤會話。 舉個例子,用戶A購買了一件商品放入購物車內, 當再次購買商品時服務器已經沒法判斷該購買行爲是屬於用戶A的會話仍是用戶B的會話了。 要跟蹤該會話,必須引入一種機制。緩存
Cookie就是這樣的一種機制。它能夠彌補HTTP協議無狀態的不足。 在Session出現以前,基本上全部的網站都採用Cookie來跟蹤會話。安全
Set-Cookie和Cookie
兩個Http頭部和Cookie有關 : Set-Cookie和Cookie服務器
當服務器返回給客戶端一個Http響應信息時,其中若是包含Set-Cookie這個頭部,說明:cookie
指示客戶端創建一個cookie
在後續的Http請求中自動發送這個cookie到服務器端,直到這個cookie過時。
若是cookie的生存時間是整個會話期間的話,那麼瀏覽器會將 cookie 保存在內存中, 瀏覽器關閉時就會自動清除這個cookie。
若是將 cookie 保存在客戶端的硬盤中,瀏覽器關閉的話,該 cookie 也不會被清除, 下次打開瀏覽器訪問對應網站時,這個cookie就會自動再次發送到服務器端。
一個cookie的設置以及發送過程分爲如下四步:網絡
客戶端發送一個http請求到服務器端
服務器端發送一個http響應到客戶端,其中包含Set-Cookie頭部
客戶端發送一個http請求到服務器端,其中包含Cookie頭部
服務器端發送一個http響應到客戶端
在客戶端的第二次請求中包含Cookie頭部,提供給了服務器端能夠用來惟一標識客戶端身份的信息。 這時,服務器端也就能夠判斷客戶端是否啓用了cookie。 儘管,用戶可能在和應用程序交互的過程當中忽然禁用cookie的使用, 可是,這個狀況基本是不太可能發生的,因此能夠不加以考慮,這在實踐中也被證實是對的。
Cookie的不可跨域名性
不少網站都會使用Cookie。例如,Google會向客戶端頒發Cookie,Baidu也會向客戶端頒發Cookie。 那瀏覽器訪問Google會不會也攜帶上Baidu頒發的Cookie呢?或者Google能不能修改Baidu頒發的Cookie呢?
答案是否認的。Cookie具備不可跨域名性。 根據Cookie規範,瀏覽器訪問Google只會攜帶Google的Cookie,而不會攜帶Baidu的Cookie。 Google也只能操做Google的Cookie,而不能操做Baidu的Cookie。
Cookie在客戶端是由瀏覽器來管理的。 瀏覽器可以保證Google只會操做Google的Cookie而不會操做Baidu的Cookie,從而保證用戶的隱私安全。 瀏覽器判斷一個網站是否能操做另外一個網站Cookie的依據是域名。 Google與Baidu的域名不同,所以Google不能操做Baidu的Cookie。
注意:
雖然網站http://images.google.com與網...://www.google.com同屬於Google, 可是域名不同,兩者一樣不能互相操做彼此的Cookie。
用戶登陸網站http://www.google.com以後會發...://images.google.com時登陸信息仍然有效,而普通的Cookie是作不到的。 這是由於Google作了特殊處理。
簡單案例 : 記錄上次訪問時間
cookie的API
new Cookie(String key,String value);
String getName();//獲取cookie的key(名稱)
String getValue();//獲取cookie的值
void setMaxAge(int);//設置cookie在瀏覽器存活時間,單位:秒
//若是設置成0:表示刪除高cookie(前提:路徑必須一致)
void setPath(String path);//設置cookie的路徑
//當咱們訪問的路徑中包含次cookie的path,纔會攜帶cookie
//默認訪問路徑:訪問Servlet的路徑,從"/項目名稱"開始,到最後一個"/"結束。好比:/demo/a/b,默認路徑爲/demo/a
//手動設置路徑:以"/項目名稱"開始,以"/"結尾
寫回瀏覽器
response.addCookie(Cookie);
獲取cookie
Cookie[] request.getCookies();
核心代碼:
/**
*/
public class CookieUtils {
public static Cookie getCookieByName(String name,Cookie[] cookies){ if(cookies != null){ for(Cookie cookie : cookies){ if(name.equals(cookie.getName())){ return cookie; } } } return null; }
}
public class RecordServlet extends HttpServlet {
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.設置編碼 response.setContentType("text/html;charset=utf-8"); PrintWriter w = response.getWriter(); //2.獲取指定名稱的Cookie Cookie cookie = CookieUtils.getCookieByName("record",request.getCookies()); //3.判斷cookie是否爲空; // 若爲null,則說明是第一次訪問; // 若不爲 null,則根據cookie顯示上一次的訪問時間 if(cookie == null){ w.write("這是您第一次訪問"); }else{ long lastTime= Long.parseLong(cookie.getValue()); w.write("您上次訪問的時間:"+ new Date(lastTime).toLocaleString()); } //4.記錄當前訪問時間,而且該信息存入cookie中 Cookie c = new Cookie("record",System.currentTimeMillis()+""); //設置cookie的有效期是 1 小時 c.setMaxAge(60*60); response.addCookie(c); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); }
}
該案例完整代碼
Cookie案例 : 瀏覽記錄
核心代碼1:記錄商品瀏覽記錄
/**
*/
public class CategoryServlet extends HttpServlet {
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲取當前訪問商品的id String id = request.getParameter("id"); Cookie c = CookieUtils.getCookieByName("ids",request.getCookies()); //判斷該 cookie 是否爲空 String ids=""; if(c == null){ //若爲空,說明以前沒有訪問記錄 //將當前商品的id放入ids中 ids = id; }else{ //若不爲空,獲取值。也就是以前瀏覽的商品編號,使用 "-"進行鏈接 ids = c.getValue(); //將 ids 經過"-"進行分割,而後存入list中,方便後續的操做 String[] categoryIds = ids.split("-"); LinkedList<String> categories = new LinkedList<>(); if(categories != null){ for(String categoryId : categoryIds){ categories.add(categoryId); } } //判斷以前記錄中有無該商品 if(categories.contains(id)){ //如有,刪除原來的id,將當前的id放入前面 categories.remove(id); }else{ // 若沒有 // 繼續判斷長度是否>=3 // 若>=3,移除最後一個,將當前的id放入最前面 // 若<3,直接將當前的id放入最前面. if(categories.size() >= 3){ categories.removeLast(); } } //無論如何,id都是最新瀏覽的,直接加入到前面 categories.addFirst(id); ids=""; for(String categoryId : categories){ ids += (categoryId + "-"); } ids = ids.substring(0,ids.length()-1); } //建立cookie c=new Cookie("ids",ids); //設置訪問路徑 c.setPath(request.getContextPath()+"/"); //設置存活時間 c.setMaxAge(60); //寫回瀏覽器 response.addCookie(c); //跳轉到指定的商品頁面上 response.sendRedirect(request.getContextPath()+"/category_info"+id+".htm"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); }
}
顯示瀏覽記錄
<ul style="list-style: none;">
<% //獲取指定名稱的cookie ids Cookie c= CookieUtils.getCookieByName("ids", request.getCookies()); //判斷ids是否爲空 if(c==null){ %> <h2>暫無瀏覽記錄</h2> <% }else{//ids=3-2-1 String[] arr=c.getValue().split("-"); for(String id:arr){ %> <li style="width: 150px;height: 216;float: left;margin: 0 8px 0 0;padding: 0 18px 15px;text-align: center;"><img src="category/0<%=(Integer.parseInt(id)-1) %>.jpg" width="130px" height="130px" /></li> <% } } %>
</ul>
核心代碼2:清空瀏覽記錄
/**
*/
public class ClearServlet extends HttpServlet {
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie c = new Cookie("ids",""); //cookie的路徑與 CategoryServlet中的cookie中的路徑要相同 c.setPath(request.getContextPath()+"/"); //直接將cookie設置成無效 c.setMaxAge(0); response.addCookie(c); //重定向 response.sendRedirect(request.getContextPath()+"/category_list.jsp"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); }
}
注意:
cookie是不能跨瀏覽器的
cookie不支持中文,須要編碼
Session
Session簡介
Session是一種記錄客戶狀態的機制,不一樣於Cookie的是Cookie保存在客戶端瀏覽器中,而Session保存在服務器上。 客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄在服務器上。這就是Session。 客戶端瀏覽器再次訪問時只須要從該Session中查找該客戶的狀態就能夠了。
若是說Cookie機制是經過檢查客戶身上的"通行證"來肯定客戶身份的話, 那麼Session機制就是經過檢查服務器上的"客戶明細表"來確認客戶身份。 Session至關於程序在服務器上創建的一份客戶檔案, 客戶來訪的時候只須要查詢客戶檔案表就能夠了。
Session機制
一方面,咱們能夠把客戶端瀏覽器與服務器之間一系列交互的動做稱爲一個 Session。 從這個語義出發,咱們會提到Session持續的時間,會提到在Session過程當中進行了什麼操做等等。
另外一方面,Session指的是服務器端爲客戶端所開闢的存儲空間,該空間保存的信息就是用於保持狀態。 從這個語義出發,咱們則會提到往Session中存放什麼內容,如何根據鍵值從Session中獲取匹配的內容等。
要使用Session,固然是先要建立Session。那麼Session在什麼時候建立呢?
Session在服務器端程序運行的過程當中建立的,不一樣語言實現的應用程序有不一樣建立Session的方法, 在Java中是經過調用HttpServletRequest的getSession方法(使用true做爲參數)建立的。 建立Session的同時,服務器會爲該Session生成惟一的session id, 這個session id在隨後的請求中會被用來從新得到已經建立的Session
Session被建立以後,就能夠調用Session相關的方法往Session中增長內容了, 而這些內容只會保存在服務器中,發到客戶端的只有session id
當客戶端再次發送請求的時候,會將這個session id帶上, 服務器接受到請求以後就會依據session id找到相應的Session,從而再次使用Session。
Session的生命週期
Session保存在服務器端。爲了得到更高的存取速度,服務器通常把Session放在內存中。 每一個用戶都會有一個獨立的Session。 若是Session內容過於複雜,當大量客戶訪問服務器時可能會致使內存溢出。 所以,Session裏的信息應該儘可能精簡。
Session在用戶第一次訪問服務器的時候自動建立。 須要注意只有訪問JSP、Servlet等程序時纔會建立Session, 只訪問HTML、IMAGE等靜態資源並不會建立Session。 若是還沒有生成Session,也可使用request.getSession(true)強制生成Session。
Session生成後,只要用戶繼續訪問,服務器就會更新Session的最後訪問時間,並維護該Session。 用戶每訪問服務器一次,不管是否讀寫Session,服務器都認爲該用戶的Session"活躍(active)"了一次。
Session的有效期
因爲會有愈來愈多的用戶訪問服務器,所以Session也會愈來愈多。 爲防止內存溢出,服務器會把長時間內沒有活躍的Session從內存刪除。 這個時間就是Session的超時時間。若是超過了超時時間沒訪問過服務器,Session就自動失效了。
Session的超時時間爲maxInactiveInterval屬性, 能夠經過對應的getMaxInactiveInterval()獲取,經過setMaxInactiveInterval(longinterval)修改。
Session的超時時間也能夠在web.xml中修改。 另外,經過調用Session的invalidate()方法可使Session失效。
Session案例 : 購物車
獲取Session
HttpSession getSession(); //request.getSession()
域對象
xxxAttribute //存放私有數據
域對象生命週期
建立:第一次調用request.getSession()
銷燬:
服務器非正常關閉;
session超時;
默認超時時間:30 min
手動設置超時:setMaxInactiveInterval(int) (單位:秒)
手動設置;
Session接口中的invalidate()方法
public void invalidate()
核心代碼1:將商品添加到購物車
public class CartServlet extends HttpServlet {
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out=response.getWriter(); //1.獲取商品名稱 String name = request.getParameter("name"); //2.獲取購物車,實際上就是存入session的map HashMap<String,Integer> map = (HashMap<String, Integer>) request.getSession().getAttribute("cart"); Integer num = null; //3.判斷購物車是否爲空 if(map==null){ //3.1 購物車爲空,說明是第一次將商品放入購物車 //先建立購物車, map = new HashMap<>(); request.getSession().setAttribute("cart",map); num = 1; }else{ //3.2 購物車不爲空,判斷該商品以前是否已經加入購物車 num = map.get(name); if(num == null){ //num==null,說明該商品以前未加入購物車 num = 1; }else{ num ++ ; } } map.put(name,num); //4.提示信息 out.print("<center>已經將<b>"+name+"</b>添加到購物車中<hr></center>"); out.print("<center><a href='"+request.getContextPath()+"/category_list.jsp'>繼續購物</a></center><br/>"); out.print("<center><a href='"+request.getContextPath()+"/cart.jsp'>查看購物車</a><center>"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); }
}
顯示購物車中信息
<body>
<div class="container"> <a href="${pageContext.request.contextPath}/category_list.jsp">繼續購物</a> <a href="${pageContext.request.contextPath}/clearCart">清空購物車</a> <div class="row"> <div style="margin:0 auto; margin-top:10px;width:950px;"> <strong style="font-size:16px;margin:5px 0;">訂單詳情</strong> <table class="table table-bordered"> <tbody> <tr class="warning" align="center"> <th>商品</th> <th>數量</th> </tr> <% HashMap<String,Integer> map = (HashMap<String,Integer>)request.getSession().getAttribute("cart"); if(map==null){ out.print("<tr><th colspan='2'>親,購物車空空,先去逛逛~~</th></tr>"); }else{ for(String name : map.keySet()){ out.print("<tr class='active'>"); out.print("<td width='30%'>"); out.print(name); out.print("</td>"); out.print("<td width='20%'>"); out.print(map.get(name)); out.print("</td>"); out.print("</tr>"); } } %> </tbody> </table> </div> </div> </div>
</body>
核心代碼2:清空購物車
public class ClearCartServlet extends HttpServlet {
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().invalidate(); response.sendRedirect(request.getContextPath()+"/cart.jsp"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); }
}
Coookie和Session案例的完整代碼
實現會話跟蹤的技術
Cookie
向客戶端發送Cookie :
Cookie c =new Cookie("name","value"); //建立Cookie
c.setMaxAge(606024); //設置最大時效,此處設置的最大時效爲一天
response.addCookie(c); //把Cookie放入到HTTP響應中
從客戶端讀取Cookie :
String name ="name";
Cookie[]cookies =request.getCookies();
if(cookies !=null){
for(int i= 0;i<cookies.length;i++){
Cookie cookie =cookies[i]; if(name.equals(cookis.getName())) //something is here. //you can get the value cookie.getValue();
}
}
優勢: 數據能夠持久保存,不須要服務器資源,簡單,基於文本的Key-Value
缺點: 大小受到限制,用戶能夠禁用Cookie功能,因爲保存在本地,有必定的安全風險。
URL 重寫
在URL中添加用戶會話的信息做爲請求的參數, 或者將惟一的會話ID添加到URL結尾以標識一個會話。
優勢: 在Cookie被禁用的時候依然可使用
缺點: 必須對網站的URL進行編碼,全部頁面必須動態生成,不能用預先記錄下來的URL進行訪問。
隱藏的表單域
<input type="hidden" name ="session" value="..."/>
優勢: Cookie被禁時可使用
缺點: 全部頁面必須是表單提交以後的結果。
session
當一個用戶第一次訪問某個網站時會自動建立 HttpSession,每一個用戶能夠訪問他本身的HttpSession。
能夠經過HttpServletRequest對象的getSession方法得到HttpSession。 經過HttpSession的setAttribute方法能夠將一個值放在HttpSession中, 經過調用 HttpSession對象的getAttribute方法,同時傳入屬性名就能夠獲取保存在HttpSession中的對象。
與上面三種方式不一樣的是,HttpSession放在服務器的內存中,所以不要將過大的對象放在裏面。 即便目前的Servlet容器能夠在內存將滿時將 HttpSession 中的對象移到其餘存儲設備中,可是這樣勢必影響性能。 添加到 HttpSession 中的值能夠是任意Java對象,這個對象最好實現了 Serializable接口, 這樣Servlet容器在必要的時候能夠將其序列化到文件中,不然在序列化時就會出現異常。
Cookie和Session的的區別
HTTP協議是無狀態的協議,服務端須要記錄用戶的狀態,就須要用某種機制來識別具體的用戶,這個機制就是Session。 Session典型的應用場景就是購物車,當點擊下單按鈕時,因爲HTTP協議無狀態,因此並不知道是哪一個用戶操做的, 因此服務端要爲特定的用戶建立了特定的Session,用於標識這個用戶,而且跟蹤用戶,這樣才知道購物車裏面的商品狀況。 這個Session是保存在服務端的,有一個惟一標識。在服務端保存Session的方法不少,內存、數據庫、文件都有。 集羣的時候也要考慮Session的轉移,在大型的網站,通常會有專門的Session服務器集羣, 用來保存用戶會話,這個時候 Session 信息都是放在內存的,此外,一些緩存服務好比Memcached之類的來放 Session。
服務端使用Cookie來識別特定的客戶。每次HTTP請求的時候,客戶端都會發送相應的Cookie信息到服務端。 實際上大多數的應用都是用 Cookie 來實現Session跟蹤的, 第一次建立Session的時候,服務端會在HTTP協議中告訴客戶端,須要在 Cookie 裏面記錄一個session id, 之後每次請求把這個 session id發送到服務器,這樣就可使用對應的Seesion了。 若是客戶端的瀏覽器禁用了 Cookie 怎麼辦? 通常這種狀況下,會使用一種叫作URL重寫的技術來進行會話跟蹤, 即每次HTTP交互,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的參數,服務端據此來識別用戶。
Cookie其實還能夠用在一些方便用戶的場景下, 設想你某次登錄過一個網站,下次登陸的時候不想再次輸入帳號了,怎麼辦? 這個信息能夠寫到Cookie裏面,訪問網站的時候, 網站頁面的腳本能夠讀取這個信息,就自動幫你把用戶名給填了, 可以方便一下用戶。這也是Cookie名稱的由來,給用戶的一點甜頭。
總結:
Session是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據能夠保存在集羣、數據庫、文件中。
Cookie是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現Session的一種方式。
免費Java高級資料須要本身領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分佈式等教程,一共30G。
傳送門:https://mp.weixin.qq.com/s/Jz...