## Cookie
### 会话技术
* 客户端浏览器第一次给服务器发送请求,会建立会话,直到某一方断开为止,一次会话中包含多次请求和响应
* Http 协议是无状态的,也就是多次请求之间无共享数据,但是使用会话技术,可以在一次会话的多次请求间进行数据共享
* Cookie 是一种客户端会话技术,它将数据保存到客户端,以便于多次请求间数据共享
* Cookie 的实现基于请求头的 `Cookie` 和响应头的 `Set-Cookie`
### Cookie 对象
获取 Cookie 对象,并绑定数据,一个 Cookie 对象可以看做是一个键值对
```java
public Cookie(String name, String value); // Cookie 的构造方法
```
通过 `ServletResponse` 对象的 `void addCookie(Cookie cookie)` 方法发送 Cookie,例如
```java
respons.addCookie(new Cookie("msg","hello cookie"));
```
通过 `ServletRequest` 对象的 `Cookie[] getCookies()` 方法获取 Cookie 数组,例如
```java
Cookie[] cookies = request.getCookies();
```
**实例**
在 demo1 中设置 Cookie,在 demo2 中获取 Cookie 并打印
```java
@WebServlet(urlPatterns = {"/cookieDemo1"})
public class CookieDemo1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie c1 = new Cookie("msg1","cookie1");
Cookie c2 = new Cookie("msg2","cookie2");
response.addCookie(c1);
response.addCookie(c2);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
@WebServlet(urlPatterns = {"/cookieDemo2"})
public class CookieDemo2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
for(Cookie c : cookies) {
System.out.println(c.getName() + " : " + c.getValue());
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
```
先访问 demo1,再访问 demo2,可以在控制台看到所有的 Cookie 都被成功打印
### Cookie 的生命周期
* 在默认情况下,当浏览器关闭后,本次会话的 Cookie 数据就会被销毁
* 可以通过 `Cookie` 对象的 `setMaxAge(int seconds)` 方法设置 Cookie 的保存时间
1. 参数 `seconds` 设置为正整数,浏览器会将 Cookie 数据写到硬盘的文件中进行持久化存储,并指定该 Cookie 的存活时间,时间到后,Cookie 文件自动失效
2. 参数 `seconds` 设置为负整数,默认情况,即关闭浏览器后,Cookie 被销毁
3. 参数 `seconds` 设置为 `0`,告知浏览器删除 Cookie 信息
### Cookie 的编码问题
* Tomcat8 以后,Cookie 可以存储中文
* Cookie 不支持存储特殊字符,例如标点和空格,如果把 demo1 中的 Cookie 值改成 `"hello cookie1!"`,则访问该 Servlet 时会直接报 `500` 错误(实现环境:Tomcat9,Chrome 浏览器)

解决方法是,在存储 Cookie前,先将数据进行 URL 编码,这样就会被还原为原始的字符串
```java
String msg1 = URLEncoder.encode("hello cookie1!","utf-8");
Cookie c1 = new Cookie("msg1",msg1);
```
此时,该 Cookie 的值是经过 URL 编码后的值,在打印时,需要再次进行 URL 解码
```java
Cookie[] cookies = request.getCookies();
for(Cookie c : cookies) {
String value = c.getValue();
if("msg1".equals(c.getName())) {
value = URLDecoder.decode(c.getValue(),"utf-8");
}
System.out.println(c.getName() + " : " + value);
}
```
### Cookie 的共享问题
1. 一个 Tomcat 服务器中,部署了多个 Web 项目
* 默认情况下各个项目之间的 Cookie 不可共享
* `Cookie` 对象的 `setPath(String path)` 方法中的参数 `path` 默认为当前项目的虚拟目录,如果设置为 `/`,则可以在多个项目中共享 Cookie
2. 不同 Tomcat 服务器中,共享 Cookie
* `Cookie` 对象的 `setDomain(String path)` 方法中的参数 `path`,如果设置为一级域名相同,则不同服务器之间可以共享 Cookie,例如
```java
Cookie c = new Cookie("msg","hello");
c.setDomain(".yzt.cool");
```
则域名 `yzt.cool` 下的所有二级域名项目如 `pan.yzt.cool`、`cfd.yzt.cool` 都可以共享该 Cookie,即使部署在不同服务器上
### Cookie 的作用
因为 Cookie 是将数据存储在客户端浏览器,且浏览器对单个 Cookie 的大小以及一个域名下的总 Cookie 数量是有限制的,所以 Cookie 一般用于存储少量的、不太敏感、不太重要的数据,例如再不登录的情况下,完成服务器对客户端的身份识别,进行一些不敏感的操作
### Cookie 案例:记住用户上次访问的时间
```java
@WebServlet(urlPatterns = {"/lastVisitTime"})
public class LastVisitTime extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String currentTime = "" + System.currentTimeMillis();
Cookie[] cookies = request.getCookies();
response.setContentType("text/html;charset=utf-8");
if(cookies!=null && cookies.length!=0) {
boolean flag = false;
for(Cookie c : cookies) {
if("LastVisitTime".equals(c.getName())) {
long time = Long.parseLong(c.getValue());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH:mm");
String date = sdf.format(new Date(time));
response.getWriter().write("
30
### Session 的特点
Session 作为一个域对象,可以用于存储一次会话中的多次请求响应的数据,可以存储任意类型任意大小的数据,且存储位置再服务器端,相对于 Cookie 可以存储更多数据而且更加安全。
## 参考
* [黑马 JavaWeb](https://www.bilibili.com/video/BV1J4411877m)

Cookie 和 Session