前言:
由于 http 的请求是一种无状态的。也就是说,即使第一次和服务器连接成功并成功登录,但是当第二次发起请求,服务器依然不知道当前的请求是哪一个用户。这是因为 在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。
由于 context 容器无法区分数据 归属哪个浏览器,也不适合
cookie 的出现就是为了解决这个问题。当第一次登录后 服务器会返回一些数据(cookie) 给浏览器,浏览器保存这些数据到本地;当用户发送第二次请求的时候,就会自动把上次请求时获得的 cookie 信息携带给服务器,而服务器则通过浏览器携带的数据判断当前用户属于哪一个。这样就避免了因为多次请求 造成数据丢失问题。
如下图案例:
cookie 保存 购物车的原理 和 步骤:
1.cookie 简介
(1) cookie 概述:
cookie 是由 HTTP 服务器设置的,保存在浏览器中 ;是客户端保存数据的一个小文件;是服务器 发送给客户端(浏览器) 的小量信息;是以键值对形式进行数据存储的。那么为什么说是小文件、小量信息呢? 需要注意的是,cookie 存储的数据量是有限的,虽然说不同的浏览器有不同的存储大小,但是一般不会超过 4kb 。因此,使用 cookie 只能存储一些小量数据。
(2) cookie 的作用:
cookie 的作用就是 识别用户的身份,唯一标识 客户端。
① 保存程序员 让浏览器保存的指定信息;② 与session 配合使用,完成会话跟踪
举个例子进行解释:
我们去商场购物的时候,会拿着会员卡,因为会员卡是一种身份的证明,证明你是此商城的老用户,并可享受一些福利待遇。 cookie 就相当于 这种会员卡,可以让服务器识别你是哪一个用户。cookie的小量数据可以帮助我们进行跟踪会话(实际业务开发中并不会单独使用 cookie 而是与 session 进行配置使用)。当然cookie也常记录跟踪购物车的商品信息(如数量)、记录用户访问次数等。
(3) cookie 的特点:
① 由服务器创建,浏览器仅仅是保存;( 但是浏览器可以采用技术手段,修改 cookie 的值 )
② 对于同一个服务器来说,只能接收 20 个Cookie;对于浏览器来说,可以一共保存 300 个cookie,且每个 cookie 的大小,不大于 4 kb。
③ cookie 默认情况下,仅再一次会话中生效(一个浏览器 从打开到关闭,注意:不是标签打开关闭),但可以设置存活时间;
会话:用户打开浏览器,点击多个连接,访问服务器的多个 web 资源,然后关闭浏览器,整个过程称之为一次会话。
④ cookie 不能直接保存中文;( 切记切记切记)
如果业务需要 保存中文,当然也有解决方案:
(4) cookie 的原理:
客户端请求服务器时,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。而客户端浏览器会把Cookie保存起来。当浏览器再请求服务器时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器通过检查该Cookie来获取用户状态。
(5) cookie 的分类:
cookie分为会话cookie和持久cookie:
会话cookie是指在不设定它的生命周期expires时的状态,浏览器的开启到关闭就是一次会话,当关闭浏览器时,会话cookie就会跟随浏览器而销毁。当关闭一个页面时,不影响会话cookie的销毁。会话cookie就像我们没有办理积分卡时,单一的买卖过程,离开之后,信息则销毁。
持久cookie则是设定了它的生命周期expires,此时,cookie像商品一样,有个保质期,关闭浏览器之后,它不会销毁,直到设定的过期时间。对于持久cookie,可以在同一个浏览器中传递数据,比如,你在打开一个淘宝页面登陆后,你在点开一个商品页面,依然是登录状态,即便你关闭了浏览器,再次开启浏览器,依然会是登录状态。这就是因为cookie自动将数据传送到服务器端,在反馈回来的结果。持久cookie就像是我们办理了一张积分卡,即便离开,信息一直保留,直到时间到期,信息销毁。
2.cookie 的创建使用
查看 java ee API :Cookie类在javax.servlet.http.Cookie包中
2.1 Cookie 的创建格式
(1) Cookie 的构造方法
Cookie(String name, String value)
name就是cookie的名称,value就是cookie要保存的值;
(2) 在服务器端,需要使用 response 对象的 addCookie 方法,将 cookie 响应给浏览器
addCookie(Cookie cookie) :void
Adds the specified cookie to the response.
2.2 Cookie 中的方法
2.2.1 常用方法:
(1) 修改 cookie 的路径:
setPath(String uri) :void
设置访问服务器的什么路径时,携带cookie; 通常直接写/
假如设置cookie的路径为 “/项目名/资源名称”, 那么访问“/项目名/资源名称”或者“/项目名/资源名称/XXXX/yyyy...” 子目录都会携带当前cookie
(2) 修改 cookie 的存活时间:(默认情况下,cookie 仅在一次会话中(从浏览器打开到浏览器关闭)中生效,时间单位是秒):
setMaxAge(int expiry) : void
设置存活时间,如果是负数,代表会话范围内有效,如果是0代表立刻死亡,如果是正数,代表存活指定的秒数;
(3) 在 request 对象中有关于获取 cookie 对象的方法:(浏览器已经有 cookie , 要在服务器上获取或修改)
getCookies() :Cookie[]
获取浏览器携带过来的所有cookie对象
(4) Cookie 的方法:(获取 cookie 对象后 使用的方法)
getName() :String
获取cookie的名称
getValue() :String
获取cookie保存的值
(5) 删除 cookie :为了通知浏览器删除哪个cookie, cookie的name属性和path属性必须一致。当MaxAge属性设置为 零 时,即通知浏览器 立即删除。
1、将cookie的name(key)保持一致,value 设置为 "";
cookie = new Cookie("username","")
2、路径要发送cookie时保持一致。
cookie.setPath("/");
3、设置存活时间为0,
cookie.setMaxAge(0)
4、将cookie发送给浏览器。
response.addCookie(cookie)
2.2.2 非常用方法:
(1) 给当前 cookie 重新赋值
setValue(String newValue):void
(2) 对该cookie进行描述的信息(说明作用),浏览器显示cookie信息时能看到
setComment(String purpose):void
(3) 域名正则 使用
setDomain(String pattern):void
符合该pattern(域名正则)的就可以访问该Cookie的域名。如果设置为“.google.com”,则所有
以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”
(4) 设置只通过 http 进行访问
setHttpOnly(boolean httpOnly):void
设为true后,只能通过http访问,javascript无法访问,还可防止xss读取cookie
(5) 是否使用安全传输协议
setSecure(boolean flag):void
是否使用安全传输协议。为true时,只有当是https请求连接时cookie才会发送给服务器端,而http时不会,但
是服务端还是可以发送给浏览端的。
注意: 如果cookie值为Unicode字符,需要为字符编码。如果cookie值为二进制数据,则需要使用BASE64编码
(6)
()()()()()()
2.3 Cookie 的代码实现
package cookieANDession;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* cookie 的创建 与 添加到浏览器的方法
*/
public class CookieStudy extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建 cookie 对象
Cookie cookie = new Cookie("liu", "hello cookie");
//3.设置当前工程生效(设置路径)
cookie.setPath("/");//当前项目有效
//4.设置 cookie 存活3秒
cookie.setMaxAge(3);//单位是秒
//2.添加的响应对象中
response.addCookie(cookie);
response.getWriter().println("ok");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
(1) Servlet + Tomcat 集成配置 : ---------------------------------此处是超链接--------------------------
(2) 页面显示效果
(3) F12 查看
2.4 服务器获取浏览器中已经存在的 cookie (根据常用方法)
(1) 创建 三个 cookie,代码实现,使浏览器保存这三个 cookie
package cookieANDession;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 创建三个cookie ,并保存到浏览器中
*/
public class CookieStudy2 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建 三 个 cookie 对象
Cookie cookie1 = new Cookie("c1", "cookie1你好");//这样会报一个错误,下面有讲解
Cookie cookie2 = new Cookie("c2", "cookie2你好");
Cookie cookie3 = new Cookie("c3", "cookie3你好");
//2.分别设置 cookie 的路径 和 存活时间
cookie1.setPath("/");
cookie2.setPath("/");
cookie3.setPath("/");
cookie1.setMaxAge(60);//一分钟
cookie2.setMaxAge(60);
cookie3.setMaxAge(60);
//3.添加 cookie
response.addCookie(cookie1);
response.addCookie(cookie2);
response.addCookie(cookie3);
response.getWriter().println("创建三个 cookie 完成");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
(2) 在服务器中获取 浏览器中 保存的 cookie,代码实现
package cookieANDession;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.plaf.synth.SynthSeparatorUI;
/**
* 服务器获取浏览器中已经存在的 cookie
*/
public class CookieStudy3 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.面向 request 对象,获取所有的 cookie 对象
Cookie[] cookies = request.getCookies();
//2.迭代数组
for (Cookie cookie : cookies) {
System.out.println("cookie名称:"+cookie.getName());
System.out.println("cookie值:"+cookie.getValue());
System.out.println("--------------");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
(3) 上面的代码运行完成后,会有以下问题:
问题1:访问 CookieStudy2
原因就是,cookie 不能保存中文造成的。我们在创建 cookie 的时候,给 cookie 的值中有 "你好" 这两中文汉字
解决方案:修改 创建 cookie 的代码
方案一:URLEncoder.encode():浏览器可以自动进行解码。
//1.创建 三 个 cookie 对象
Cookie cookie1 = new Cookie("c1", "cookie1"+URLEncoder.encode("你好","utf-8"));
Cookie cookie2 = new Cookie("c2", "cookie2"+URLEncoder.encode("你好","utf-8"));
Cookie cookie3 = new Cookie("c3", "cookie3"+URLEncoder.encode("你好","utf-8"));
方案二:使用getByte():存字节数组。但是这样比较麻烦,还要 手动创建字节数组进行解码。
问题2:访问 CookieStudy2
上述情况是,cookie 已经成功保存到浏览器,但显示到浏览器页面上的内容出现乱码。
解决方案: 在添加 cookie 的服务端 中的 doGet() 方法中 添加下面的代码
response.setContentType("text/html;charset=utf-8");
问题3:访问 CookieStudy3
控制台出现上述乱码的原因是:在创建 cookie 的时候,使用 URLEncoder.encode()进行了编码,而在获取的时候没有对其进行解码操作。
解决方案:URLDecoder.decode() 进行解码,修改 CookieStudy3 中获取 cookie 值的代码
System.out.println("cookie值:"+URLDecoder.decode(cookie.getValue(),"UTF-8"));
2.5 对 cookie 的值进行修改,其实就是 cookie 的覆盖问题
添加代码 在 CookieStudy3 中 for 循环中添加一下代码
//修改:如果 cookie 等于 c3 就对其进行修改值
if(cookie.getName().equals("c3")) {
Cookie coo = new Cookie("c3", URLEncoder.encode("我是新的c3", "utf-8"));
coo.setPath("/");
coo.setMaxAge(60);
response.addCookie(coo);
}
修改 cookie 注意事项:
① cookie 的名字要保持一致;② cookie 的路径要保持一致
修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie而不会覆盖之前的Cookie,从而导致修改、删除失败。
2.6 案例分析:
案例一:记录上一次的登录时间(七天内免登陆)
(1) 需求:
当 7 天内访问该 servlet 时,提示上一次访问的时间,如果没有上一次的登录时间,则提示是第一次登录
(2) 分析:
在 servlet 中先获取 cookie 对象,如果能找到,则显示对应的上一次登陆的时间,如果找不到则显示第一次登录。更新 cookie,创建一个同名的 cookie 对象,覆盖原来的 cookie 对象。(如果想要永久性的,可以把 cookie 存储到数据库里面,每次都去数据库里面读取即可)
(3) 代码实现
package cookieANDession;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 记录上次访问时间,并创建新的cookie 覆盖上次的访问时间
*/
public class CookieHistory extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1. 获取 cookie 对象
Cookie[] cookies = request.getCookies();
//2.迭代 cookie 数组,获取我们想要的 cookie 对象
String msg = "亲,您是第一次访问此网站,或上一次的时间间隔超过了7天!!!";
if(cookies != null) {
for (Cookie cookie : cookies) {
//3.如果找不到想要的 cookie 对象,则提示是第一次登录,如果找到了则提示上一次的登录时间
//根据cookie 的名称 查找对应的 cookie
if(cookie.getName().equals("his")) {
msg=URLDecoder.decode(cookie.getValue(), "utf-8");
}
}
}
//将上面的信息 响应给浏览器
response.getWriter().println(msg);
//4.更新 cookie ,创建一个新的 cookie 对象,并覆盖原来的 cookie 对象
Cookie coo = new Cookie("his", URLEncoder.encode(new Date().toLocaleString(),"utf-8"));
coo.setPath("/");
coo.setMaxAge(60*60*24*7);
response.addCookie(coo);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2.7 cookie 的 扩展 (网上截取)
(1) cookie 的 setPath() 路径问题
默认情况下cookie只能在一个应用中共享,即一个cookie只能由创建它的应用获得。
设置同一服务器内的cookie使用范围用setPath
c.setPath(“/”),使同一服务器内所有应用都可访问到该Cookie:
Cookie c = new Cookie("name","peter");
c.setMaxAge(24*60*60);
c.setPath("/");//同一服务器内所有应用都可访问到该Cookie
response.addCookie(c); //保存cookie到客户端
如果同一服务器内有两个应用agx1.0和agx2.0
当我们在agx1.0里有c.setPath("/agx2.0/");时,该cookie就只能在agx2.0下面能获取到,就连创建该cookie的agx1.0也获取不到。
如果在agx1.0里有c.setPath("/agx2.0/abc/");时,则只能在agx2.0/abc下面才能获得该cookie,即使在agx2.0下面也不能获取到。
(2) cookie 的 setDomain() 跨域问题
① 正常情况下Cookie不可跨域名,如域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。即使在同一个一级域名下的两个二级域名如www.agx.com和images.agx.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有agx.com名下的二级域名都可以使用该Cookie,则需要设置Cookie的domain参数为”.agx.com”,例如:
Cookie cookie = new Cookie("name","peter"); // 新建Cookie
cookie.setDomain(".agx.com"); // 设置域名
cookie.setPath("/"); // 设置路径
cookie.setMaxAge(Integer.MAX_VALUE); // 设置有效期为永久
response.addCookie(cookie); // 输出到客户端
读者可以修改本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个临时域名,然后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。
注意:domain参数必须以点(“.”)开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个name相同的Cookie,domain属性分别为两个域名。
② 若A服务器的域名为:adv.audiogroup.com,有应用名为:agx1.0; B服务器的域名为:agx.com,有应用名为:agx2.0。
在A服务器的agx1.0应用下设置cookie如下:
Cookie cookie = new Cookie("name","peter"); // 新建Cookie
cookie.setDomain(".agx.com"); // 设置域名
cookie.setPath("/"); // 设置路径
cookie.setMaxAge(Integer.MAX_VALUE); // 设置有效期为永久
response.addCookie(cookie); // 输出到客户端
这时,在B服务器下的agx2.0应用和agx1.0里都能取到上面的Cookie。
注:输入URL访问agx2.0时,必须输入域名才能获取其它服务器共享给它的cookie,如:
输入http://images.agx.com:8080/agx2.0,可以获取agx1.0在客户端设置的cookie
输入:http://localhost:8080/agx2.0则不可以获得cookie。
③ setPath()与setDomain()的区别?
setDomain()主要用来确定两个不同名称但后缀相同的网站地址是否能使用同一个Cookie。
例: www.agx.com和 bbs.agx.com只要有cookie.setDomain(".agx.com");就都能使用该cookie
setPath()主要用来确定地址里什么后缀下能够使用这个Cookie
归结起来就是:setDomain决定允许访问Cookie的域名,而setPath决定允许访问Cookie的路径(ContextPath)
获取用户请求里的cookie
Cookie[] cookie = request.getCookies();//获取的是请求里的所有cookie组成的数组
for(int i=0;i<cookie.length;i++){
if("name".equals(cookie[i].getName())){
System.out.println(cookie[i].getValue());//得到peter
break;
}
}
2.8 谷歌浏览器查看 cookie 信息
(1) 查看 浏览器中已经存在的 cookie
(2) 通过 谷歌浏览器 抓取 传输的 cookie 信息
按 F12