Cookie 与 Session概述
在 Web 开发中,会话跟踪是一个核心问题。HTTP 协议是无状态的,这意味着服务器无法直接记住客户端的状态。而 Cookie 和 Session 技术的出现,正是为了解决这一难题。
一、Cookie概述
Cookie,翻译成中文是小甜点、小饼干的意思。在 HTTP 协议中,它代表着服务器送给客户端浏览器的 “小甜点”。实际上,Cookie 就是由一个键和一个值构成的信息,会随着服务器端的响应发送给客户端浏览器。客户端浏览器会将其保存起来,等下一次访问该服务器时,再把 Cookie 发送回服务器。
Cookie 的规范
不用担心 Cookie 会占满你的硬盘,因为它有严格的规范限制:
- 一个 Cookie 最多只有 4KB 大小。
- 浏览器最多可以保存 300 个 Cookie。
不过,在浏览器竞争的大环境下,一些浏览器对 Cookie 规范进行了 “扩展”,比如有的浏览器允许每个 Cookie 为 8KB,最多可保存 500 个 Cookie,但即便如此,也不会出现占满硬盘的情况。另外,不同的浏览器之间是不能共享 Cookie 的。
Cookie 的作用
Cookie 的核心作用就是 “跟踪客户端状态”。服务器把信息保存在客户端,客户端下次请求时再把这些信息还给服务器,服务器就能通过这些信息识别客户端了。
Cookie 的基本操作
- 保存 Cookie 到客户端:这是响应工作的一部分,通过 HttpServletResponse 类的
void addCookie(Cookie c)
方法来实现,该方法可多次调用以添加多个 Cookie。例如:
Cookie cookie = new Cookie("username", "txjava");
response.addCookie(cookie);
这样,在响应头中就会添加 Set - Cookie 的值,客户端浏览器也会保存该 Cookie。当下一次访问服务器时,请求头中就会带着这个 Cookie 的值。
- 服务器端读取 Cookie:浏览器保存 Cookie 后,会在下次请求时把 Cookie 放到请求头中发送给服务器,服务器可通过 HttpServletRequest 的
Cookie[] getCookies()
方法读取。示例如下:
Cookie[] cookies = request.getCookies();
if(cookies != null){for(Cookie cookie : cookies){System.out.println(cookie.getName() +":"+ cookie.getValue());}
}
Cookie 的生命周期
Cookie 的存活时间取决于其生命周期设置,默认情况下,Cookie 只在浏览器内存中存活,关闭浏览器后就会消失。可通过Cookie#setMaxAge(int expiry)
方法设置存活时间(参数为秒数):
cookie.setMaxAge(60*60*24*30*12)
:表示 Cookie 可存活 1 小时(此处原文档描述有误,60*60 才是 1 小时),此时浏览器不仅会把 Cookie 保存在内存中,还会保存到硬盘上,即便关闭浏览器、重启电脑,Cookie 也会存在 1 小时。cookie.setMaxAge(-1)
:这是默认值(其实只要是负数都一样),表示 Cookie 只在浏览器内存中存活,关闭浏览器窗口后就会消失。cookie.setMaxAge(0)
:表示 Cookie 被作废,既不在内存中存活,也不在硬盘上存活,目的是覆盖客户端原来的该 Cookie 使其失效。
Cookie 的路径
Cookie 有一个 path 属性,可通过Cookie#setPath(String)
方法设置。若不设置,其默认路径就是请求的路径。比如请求http://localhost:8080/cookie_demo/path
时,服务器响应的 Cookie 默认路径是/cookie_demo
;请求http://localhost:8080/cookie_demo/path/son
时,默认路径是/cookie_demo/path
。
Cookie 路径的作用是决定服务器的请求是否会从浏览器中加载某些 Cookie。例如,有两个 Cookie:
- cookie1:name=path1;value=pathvalue1;path=/cookie_demo
- cookie2:name=path1;value=pathvalue2;path=/cookie_demo/path
当访问http://localhost:8080/cookie_demo/*
时,请求头中会包含 cookie1,不包含 cookie2;当访问http://localhost:8080/cookie_demo/path/*
时,请求头中会包含 cookie1 和 cookie2。也就是说,访问子路径时会包含父路径的 Cookie,访问父路径时不包含子路径的 Cookie。
若想在 BServlet 中设置的 Cookie,在客户端访问 AServlet 时也能包含在请求头中,可设置 BServlet 中 Cookie 的 path,如c2.setPath(“/cookie_demo”)
(硬编码)或c2.setPath(request.getContextPath() + “/”)
(活编码)。
Cookie 保存中文
Cookie 中不能直接设置中文,但可先使用URLEncoder.encode()
方法编码后再存放,获取时先使用URLDecoder.decode()
方法解码。
- 添加 Cookie:
Cookie cookie1 = new Cookie("username", URLEncoder.encode(username,"UTF-8"));
- 读取 Cookie:
Cookie[] cookies = request.getCookies();
if(cookies != null){for(Cookie cookie : cookies){if(cookie.getName().equals("username"))username = URLDecoder.decode(cookie.getValue(),"UTF-8");if(cookie.getName().equals("password"))password = cookie.getValue();}
}
Cookie 的浏览器管理
- Google Chrome:查看 Cookie 可通过相关入口操作;清理 Cookie 则在设置 - 隐私设置和安全性 - 清除浏览数据中进行。
- Firefox:查看 Cookie 有相应的操作入口;清理 Cookie 在选项 - 隐私与安全 - Cookie 和网站数据 - 清除数据中进行。
Cookie 的禁用
默认情况下浏览器启用 Cookie,手动禁用后,绝大多数互联网网站无法登录,这与后续要讲的 session 有关。判断浏览器是否禁用 Cookie,可通过尝试获取刚刚添加的 Cookie,若获取不到,则说明 Cookie 被禁用。
二、HttpSession:服务器端的会话对象
HttpSession 概述
Session 也是域对象之一,其范围在一个会话范围内有效,拥有getAttribute()
和setAttribute()
等系列方法。在一个会话内共享一个 session 对象,可保存会话内的数据,如当前用户的信息。
目前所学域对象的作用范围:ServletContext > HttpSession > HttpServletRequest。
获取 session 对象可使用request.getSession()
方法。有了 session,就无需再用 Cookie 跟踪会话,但 session 的生命周期相对较短,用户关闭浏览器窗口后,session 就会失效。
HttpSession 原理(依赖 Cookie)
HTTP 是无状态协议,但 session 能跟踪会话状态,这是因为 session 依赖 Cookie。
- 客户端第一次访问服务器时,服务器会为其创建一个 session 对象,放入 session 池中,并在响应时通过 Cookie 将 sessionId 发送给客户端。只有第一次访问时,服务器才会创建 session 并响应 sessionId。
- 客户端再次访问服务器时,会在请求中带着 sessionId,服务器通过 sessionId 到 session 池中找到对应的 session 对象,从而完成会话跟踪。服务器端保存 session 对象,客户端只保存 sessionId,用户在 session 中保存的数据就能被再次使用。
发送 sessionId 的 Cookie 的 maxAge 为 - 1,即只在浏览器内存中存在,关闭所有浏览器窗口后,该 Cookie 就会消失。
HttpSession 失效
session 失效主要有以下几个原因:
- 调用
session.invalidate()
方法注销 session。 - session 超时,可在配置中设置,如:
<session-config><!-- session的超时时间,以分钟为单位 --><session-timeout>1</session-timeout>
</session-config>
- Cookie 被禁用,因为 session 依赖 Cookie 传递 sessionId,Cookie 禁用后,session 也会失效(不过可通过其他方式实现,如在 url 中传递 session_id)。
三、Session 和 Cookie 的区别(面试常考)
区别 | Session | Cookie |
---|---|---|
存储位置 | 服务器端 | 客户端(浏览器) |
存储位置 | 默认存在服务器的一个文件里(不是内存) | 客户端浏览器中 |
运行依赖 | 依赖 session id,而 session id 通常存在 cookie 中 | 不依赖其他存储对象 |
存储位置 | 可放在文件、数据库、内存中 | 存于客户端浏览器 |
应用场景 | 常用于用户验证等场合 | 用于跟踪客户端状态,如保存用户偏好设置等 |
四、综合练习:使用 cookie 实现自动登陆
实现思路
通过在客户端保存包含用户名和密码信息的 Cookie,当用户再次访问时,服务器读取 Cookie 中的信息进行自动登录验证。
代码实现
- html 代码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="login" method="post">用户:<input type="text" name="username"/><br><br>密码:<input type="password" name="password"/><br><br><input type="checkbox" value="1" name="auto"/> 一天内自动登陆<br><br><input type="submit" value="登陆"/><br>
</form>
</body>
</html>
- Java 代码:
package cn.tx.servlet;import cn.tx.model.User;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;/*** 模拟自动登陆方法*/
@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取用户端提交的表单信息String username = request.getParameter("username");String password = request.getParameter("password");String auto = request.getParameter("auto");// 统一设置response响应格式及编码response.setContentType("text/html;charset=utf-8");response.setCharacterEncoding("UTF-8");// 判断是否提交用户信息if(username == null && password == null && auto == null){// 用来接收cookie的value信息String cookieValue = null;// 获取用户端cookiesCookie[] cookies = request.getCookies();// 如果cookies不为null 尝试获取name为txjavac的cookie的valueif(cookies != null){for(Cookie ck : cookies){if(ck.getName().equals("txjavac")){cookieValue = ck.getValue();}}}// 如果没有获取到cookie的value 返回信息if(cookieValue == null){response.getWriter().write("您还未进行登陆,请进行登陆!!!");return;}else{// 如果获取到// 对该信息进行解码BASE64Decoder decoder = new BASE64Decoder();cookieValue = new String(decoder.decodeBuffer(cookieValue));// 对解码后的字符串进行切分String[] split = cookieValue.split(":");// 获取用户名和密码username = split[1];password = split[2];// 创建用户对象User user = new User(username,password);// 获取sessionHttpSession session = request.getSession();// 把用户对象存储到session中session.setAttribute("user",user);// 返回内容response.getWriter().write("尊敬的"+username+",欢迎您!!!");return;}}// 判断用户名或密码是否正确if(username!=null && password!=null && username.equals("admin") && password.equals("txjava")){// 创建用户对象User user = new User(username,password);// 获取session会话对象HttpSession session = request.getSession();// 把用户存入session中session.setAttribute("user",user);// 用户名密码正确的话,判断是否勾选了一天内自动登陆if(auto != null && auto.equals("1")){// 拼接存储于cookie的value值String value = "txjava:" + username + ":" + password + ":" + 24*3600;// 进行BASE64编码BASE64Encoder encoder = new BASE64Encoder();value = encoder.encode(value.getBytes());// 创建cookie并且设置一天失效后添加到responseCookie cookie = new Cookie("txjavac",value);cookie.setMaxAge(24*3600);response.addCookie(cookie);}response.getWriter().write("尊敬的"+username+",欢迎您!!!");return;}else {response.getWriter().write("用户名或密码错误!!!");return;}}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request,response);}
}
五、学习目标回顾
- 熟练掌握 Cookie 的概念、使用及生命周期。
- 能够使用 Cookie 存储中文并了解 Cookie 的路径作用。
- 掌握 Session 的原理及使用方法。
- 能够详细说明 Cookie 和 Session 的区别。
- 能够独立完成自动登陆功能的开发。
通过以上内容,相信大家对 Cookie 和 Session 有了较为全面的认识,在实际开发中,可根据具体需求灵活运用这两种技术来实现会话跟踪等功能