Cookie意爲「甜餅」,是由W3C組織提出,最先由Netscape社區發展的一種機制。 目前Cookie已經成爲標準,全部的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。html
因爲HTTP是一種無狀態的協議,服務器單從網絡鏈接上無從知道客戶身份。 怎麼辦呢?就給客戶端們頒發一個通行證吧,每人一個,不管誰訪問都必須攜帶本身通行證。 這樣服務器就能從通行證上確認客戶身份了。這就是Cookie的工做原理。git
Cookie其實是一小段的文本信息。 客戶端請求服務器,若是服務器須要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。 客戶端瀏覽器會把Cookie保存起來。 當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。 服務器檢查該Cookie,以此來辨認用戶狀態。 服務器還能夠根據須要修改Cookie的內容。github
Cookie技術是客戶端的解決方案,Cookie就是由服務器發給客戶端的特殊信息,而這些信息以文本文件的方式存放在客戶端, 而後客戶端每次向服務器發送請求的時候都會帶上這些特殊的信息。web
具體過程以下:數據庫
Web應用程序是使用HTTP協議傳輸數據的。HTTP協議是無狀態的協議。 一旦數據交換完畢,客戶端與服務器端的鏈接就會關閉,再次交換數據須要創建新的鏈接。 這就意味着服務器沒法從鏈接上跟蹤會話。 舉個例子,用戶A購買了一件商品放入購物車內, 當再次購買商品時服務器已經沒法判斷該購買行爲是屬於用戶A的會話仍是用戶B的會話了。 要跟蹤該會話,必須引入一種機制。跨域
Cookie就是這樣的一種機制。它能夠彌補HTTP協議無狀態的不足。 在Session出現以前,基本上全部的網站都採用Cookie來跟蹤會話。瀏覽器
兩個Http頭部和Cookie有關 : Set-Cookie和Cookie緩存
當服務器返回給客戶端一個Http響應信息時,其中若是包含Set-Cookie這個頭部,說明:安全
一個cookie的設置以及發送過程分爲如下四步:bash
在客戶端的第二次請求中包含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。
雖然網站images.google.com與網站www.google.com同屬於Google, 可是域名不同,兩者一樣不能互相操做彼此的Cookie。
用戶登陸網站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();複製代碼
/**
* 根據 cookie名稱獲取Cookie 的工具類
*/
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);
}
}複製代碼
/**
* 記錄商品瀏覽記錄,只展現3個商品
*/
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>複製代碼
/**
* 清空瀏覽記錄
*/
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);
}
}複製代碼
Session是一種記錄客戶狀態的機制,不一樣於Cookie的是Cookie保存在客戶端瀏覽器中,而Session保存在服務器上。 客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄在服務器上。這就是Session。 客戶端瀏覽器再次訪問時只須要從該Session中查找該客戶的狀態就能夠了。
若是說Cookie機制是經過檢查客戶身上的"通行證"來肯定客戶身份的話, 那麼Session機制就是經過檢查服務器上的"客戶明細表"來確認客戶身份。 Session至關於程序在服務器上創建的一份客戶檔案, 客戶來訪的時候只須要查詢客戶檔案表就能夠了。
一方面,咱們能夠把客戶端瀏覽器與服務器之間一系列交互的動做稱爲一個 Session。 從這個語義出發,咱們會提到Session持續的時間,會提到在Session過程當中進行了什麼操做等等。
另外一方面,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的超時時間爲maxInactiveInterval屬性, 能夠經過對應的getMaxInactiveInterval()獲取,經過setMaxInactiveInterval(longinterval)修改。
Session的超時時間也能夠在web.xml中修改。 另外,經過調用Session的invalidate()方法可使Session失效。
獲取Session
HttpSession getSession(); //request.getSession()複製代碼
域對象
xxxAttribute //存放私有數據複製代碼
域對象生命週期
默認超時時間:30 min
手動設置超時:setMaxInactiveInterval(int) (單位:秒)
Session接口中的invalidate()方法
public void invalidate()複製代碼
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>複製代碼
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);
}
}複製代碼
向客戶端發送Cookie :
Cookie c =new Cookie("name","value"); //建立Cookie
c.setMaxAge(60*60*24); //設置最大時效,此處設置的最大時效爲一天
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中添加用戶會話的信息做爲請求的參數, 或者將惟一的會話ID添加到URL結尾以標識一個會話。
優勢: 在Cookie被禁用的時候依然可使用
缺點: 必須對網站的URL進行編碼,全部頁面必須動態生成,不能用預先記錄下來的URL進行訪問。
<input type="hidden" name ="session" value="..."/>複製代碼
優勢: Cookie被禁時可使用
缺點: 全部頁面必須是表單提交以後的結果。
當一個用戶第一次訪問某個網站時會自動建立 HttpSession,每一個用戶能夠訪問他本身的HttpSession。
能夠經過HttpServletRequest對象的getSession方法得到HttpSession。 經過HttpSession的setAttribute方法能夠將一個值放在HttpSession中, 經過調用 HttpSession對象的getAttribute方法,同時傳入屬性名就能夠獲取保存在HttpSession中的對象。
與上面三種方式不一樣的是,HttpSession放在服務器的內存中,所以不要將過大的對象放在裏面。 即便目前的Servlet容器能夠在內存將滿時將 HttpSession 中的對象移到其餘存儲設備中,可是這樣勢必影響性能。 添加到 HttpSession 中的值能夠是任意Java對象,這個對象最好實現了 Serializable接口, 這樣Servlet容器在必要的時候能夠將其序列化到文件中,不然在序列化時就會出現異常。
總結: