0x01 前言
HTTP 为无状态协议,协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传。另一方面,在服务器不需要先前信息时它的应答就较快。客户端与服务器进行动态交互的 Web 应用程序出现之后,HTTP 无状态的特性严重阻碍了这些应用程序的实现,毕竟交互是需要承前启后的,简单的购物车程序也要知道用户到底在之前选择了什么商品。于是,两种用于保持 HTTP 连接状态的技术就应运而生了,一个是 Cookie,而另一个则是 Session。
0x02 Cookie
Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。
0x03 Session
Session,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。
0x04 Java 操作 Cookie
Java 操作 Cookie 步骤
Cookie cookie = new Cookie(String name, String value); // 创建 Cookie 对象 cookie,name 为名,value 为值
resp.addCookie(cookie); // 将 cookie 发送给浏览器,由浏览器保存
Cookie[] cookies = req.getCookies(); // 获取 HTTP request 中 cookie,Cookie 类型的数组形式保存
构建登陆表单
创建用于登陆表单 login.html
<body>
<form action="/myweb/cookie/login" method="post">
<p><input type="text" name="username" placeholder="用户名" /></p>
<p><input name="password" name="password" placeholder="密码" /></p>
<p><input type="submit" value="提交表单" /></p>
</form>
</body>
创建浏览器本地 Cookie
接收 login.html 提交的登陆参数账号和密码,req.getParamter()
方法接收 request 提交的参数并赋值给 String 类型变量 username
和 password
,创建 Cookie 对象 userCookie
和 passCookie
并逐一发送给浏览器。
@WebServlet(urlPatterns = "/cookie/login")
public class LoginCookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter(); // 页面内容输出对象
String username = req.getParameter("username"); // 1、捕捉表单提交参数
String password = req.getParameter("password");
Cookie userCookie = new Cookie("username", username); // 2、创建 cookie
Cookie passCookie = new Cookie("password", password);
resp.addCookie(userCookie); // 3、发送 cookie 到客户端
resp.addCookie(passCookie);
// TODO 其他操作
}
}
0x05 服务端获取 Cookie 进行处理
reqs.getCookies()
获取 Cookie 类型数组 Cookie[] cookies
,由cookie.getName()
获取 Cookie 的名称,根据名称进行进一步操作。
@WebServlet(urlPatterns = "/cookie/handler")
public class HandlerCookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter(); // 页面内容输出对象
String username = null;
String password = null;
Cookie[] cookies = req.getCookies(); // 1、创建 cookie
if (cookies != null) { // 2、处理 cookie
for (Cookie cookie : cookies) {
String key = cookie.getName();
if ("username".equals(key)) {
username = cookie.getValue();
}
if ("password".equals(key)) {
password = cookie.getValue();
}
}
}
// TODO 进行其他操作
}
}
0x06 Java 操作 Session
Java 操作 Session 步骤:
HttpSession session = req.getSession(boolean create); // Session 的创建和获取
session.setAttribute(String name, Object value); // Session 存储数据
String username = session.getAttribute(String name); // Session 获取数据
session.removeAttribute(String name); // Session 删除某个属性值,name 为属性的名称
session.invalidate(); // 销毁 Session 对象
session.setMaxInactiveInterval(60 * 10); // 超过10分钟,销毁 session
HttpSession session = req.getSession(boolean create);
当 create
为 true
时,判断 Session 对象是否存在,存在则返回该 Session 对象,不存在则创建一个新的session
。当 create
为 false
时,不存在则返回 null
。
Session的创建、获取和操作
  接收 login.html 提交的登陆参数账号和密码,利用 JavaBean规范创建 User 类,将 `username` 和 `password` 存储在JavaBean规范生成对象中。`req.geetSession()` 创建 Session 对象 `session`,session 以属性名标识session "USER_IN_SESSION" 对应值 `user`。
@WebServlet("/session/login")
public class LoginSessionServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
User user = new User(username, password);
HttpSession session = req.getSession(); // 1、创建Session
session.setAttribute("USER_IN_SESSION", user); // 2、创建Session对象
session.removeAttribute("USER_IN_SESSION"); // 3、删除指定session属性
session.invalidate(); // 4、销毁session
session.setMaxInactiveInterval(60 * 10); // 5、超过10分钟无操作则销毁session
// TODO 进行其他操作
}
}
Session 获取具体操作
req.getSession()
创建和获取 session 对象, session.getAttribute("USER_IN_SESSION")
获取强转为 User 类对象 user
调用 getter 获取 username
,password
。
@WebServlet(urlPatterns="/session/handler")
public class DetailSessionServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
HttpSession session = req.getSession();// 1、创建和获取 Session
User user = (User) session.getAttribute("USER_IN_SESSION"); // 2、处理 Session
String username = user.getUsername(); // 3、获取对象后调用 getter 取值
String password = user.getPassword();
}
}
0x07 Java 操作 Cookie 其他细节
Cookie 原理
第一次请求时,服务器将 cookie 发送到浏览器,浏览器进行保存,下次请求时,浏览器将 cookie 通过 request 对象发送到服务器。
Cookie 支持中文存储
Cookie userCookie = new Cookie("username", URLEncoder.encode(username, "UTF-8")); // 使用 URLEncoder 编码
username = URLDecoder.decode(cookie.getValue(), "UTF-8"); // 使用 URLDecoder 解码
String username = req.getParameter("username")
接收 username
应考虑为 GET 提交还是 POST 提交,因为当提交 username 为中文时,注意编码是否为 UTF-8 以规避乱码问题。
会话 Cookie 和持久化 Cookie
会话 Cookie:生命周期伴随着会话的开始和结束。
持久化 Cookie:生命周期与会话的开始和结束无关。
cookie.setMaxAge(int expiry);
// expiry > 0 持久化 cookie
// expriy = 0 删除该 cookie
// expriy < 0 会话 cookie,默认值为 -1
Cookie 作用域
不同主机不共享 cookie。
希望不同的二级域名中可以共享 Cookie,那么就要设置 Cookie 的 domain 和 path 了。
例如:music.baidu.com、map.baidu.com、tieba.baidu.com,它们的域名不同,但百度希望它们之间可以共享 Cookie。
设置 Cookie 的 path 为 "/" ,例如:
cookie.setPath("/")
在整个 baidu.com 中都能传递。设置 Cookie 的 domain,例如:
cookie.setDomain(".baidu.com")
,其中 domain 中没有指定域名前缀!在 music.baidu.com 主机中的某个项目中保存了 Cookie。
在 map.baidu.com 主机中某个项目中获取 Cookie。
0x08 Java 操作 Session 其他细节
命名规则
一般来说,Session 我习惯命名为“XX_IN_SESSION”例如:USER_IN_SESSION
。
使用 JavaBean 对象
若需要把多个数据存放到 Session 中,就得调用 setAttribute()
N次。一般的,我们把需要存储的数据,封装成一个对象,然后在存储到 Session 中。例如:把用户的信息,封装到 User 对象 user
,再赋给 session。例如:session.setAttribute("USER_IN_SESSION", user);
网络共享 Session
如果多台服务器之间需要共享 Session ,此时 Session 中的对象,必须实现 java.io.Serializable(序列化)。
URL 带 Session
当浏览器禁用 Cookie 之后需要手动携带 Session 的 ID,通过链接 url + jessionid 进行手动携带session。例如: String url = http://www.baidu.com/example;jesession=session.session.getId();