目录
5、浏览器 Cookie 和服务器 Session 之间的关联
一、Cookie 简介
1、什么是 cookie
- Cookie 是服务器端通知客户端保存键值对的一种技术。
- Cookie 是 servlet (服务器)发送到 Web 浏览器(客户端)的少量信息,这些信息由浏览器保存,然后发送回服务器。
- 客户端有了 Cookie 后,每次请求都会发送 Cookie 给服务器。
- 每个 Cookie 的大小不能超过 4KB。
2、Cookie 的创建
(1)示例
(1-1)cookie.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Cookie</title>
<style type="text/css">
ul li {
list-style: none;
}
</style>
<base href = "http://localhost:8080/cookie_war_exploded/"/>
</head>
<body>
<iframe name="target" width="500" height="500" style="float: left;"></iframe>
<div style="float: left;">
<ul>
<li><a href="./start_CookieServlet?action=createCookie" target="target">Cookie的创建</a></li>
<li><a href="" target="target">Cookie的获取</a></li>
<li><a href="" target="target">Cookie值的修改</a></li>
<li>Cookie的存活周期</li>
<li>
<ul>
<li><a href="" target="target">Cookie的默认存活时间(会话)</a></li>
<li><a href="" target="target">Cookie立即删除</a></li>
<li><a href="" target="target">Cookie存活3600秒(1小时)</a></li>
</ul>
</li>
<li><a href="" target="target">Cookie的路径设置</a></li>
<li><a href="" target="target">Cookie的用户免登录练习</a></li>
</ul>
</div>
</body>
</html>
(1-2)CookieServlet 类:
package com.web;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class CookieServlet extends BaseServlet {
protected void createCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.创建 cookie 对象
Cookie cookie1 = new Cookie("key_1", "value_1");
Cookie cookie2 = new Cookie("key_2", "value_2");
// 2.通知客户端保存 cookie(只要是服务器要发给客户端的,都是通过响应来进行操作的)
resp.addCookie(cookie1);
resp.addCookie(cookie2);
// 3.在客户端上显示一句话
resp.getWriter().write("cookie 创建成功");
}
}
(1-3)BaseServlet 类(自定义的用来处理页面 action 属性的类):
package com.web;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Method;
public abstract class BaseServlet extends HttpServlet {
/**
* TODO 因为有些访问方式是 GET 请求,因此还需要 doGet 方法,然后再传到 doPost 方法即可
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
String action = req.getParameter("action");
try { // 通过反射,用 action 的值对应相同的函数名
/*
this.getClass() == UserServlet.class
this == new UserServlet()
*/
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
// 调用目标方法(业务)
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(2)查看 Cookie
3、服务器如何获取 Cookie
服务器获取客户端的 Cookie 只需要一行代码:
Cookie[] cookies = req.getCookies();
(1)示例:
(1-1)CookieServlet 类:
package com.web;
import com.utils.CookieUtils;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class CookieServlet extends BaseServlet {
protected void getCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies) {
resp.getWriter().write("Cookie[" + cookie.getName() + "]" + "=" + cookie.getValue() + "\n");
}
resp.getWriter().write(CookieUtils.findCookie("key_1", cookies).getValue());
}
}
(1-2)CookieUtils 类(自定义的 Cookie 工具类):
package com.utils;
import javax.servlet.http.Cookie;
public class CookieUtils {
/**
* TODO 查找指定名称(键)的 Cookie 对象
* @param name
* @param cookies
* @return
*/
public static Cookie findCookie(String name, Cookie[] cookies) {
if (name == null || cookies == null || cookies.length == 0) {
return null;
}
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie;
}
}
return null;
}
}
4、Cookie 的修改
(1)方案一
- 使用 cookie.addCookie() 覆盖原有的 cookie 键值对。
(2)方案二
- 先找到需要修改的 Cookie 对象
- 调用 setValue() 方法赋予新的 Cookie 值
- 调用 response.addCookie() 通知客户端保存修改
(2-1)示例:
记得先创建 Cookie 后再进行修改。
package com.web;
import com.utils.CookieUtils;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class CookieServlet extends BaseServlet {
protected void updateCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = CookieUtils.findCookie("key_1", req.getCookies());
if (cookie != null) {
cookie.setValue("newValue_1");
resp.addCookie(cookie);
}
resp.getWriter().write("key_1 的值已经修改");
}
}
5、Cookie 的生命控制
Cookie 的生命控制指的是如何管理 Cookie 什么时候被销毁(删除),使用 setMaxAge() 来控制。
(1)setMaxAge()
- 如果传入的参数是负数,表示关闭浏览器后,Cookie 就会被删除。(默认,-1)
- 如果传入的参数是正数,表示在指定秒数后过期。
- 如果传入的参数是 0,表示马上删除 Cookie。
(2)示例:
package com.web;
import com.utils.CookieUtils;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class CookieServlet extends BaseServlet {
protected void defaultLiveTime(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("defaultLiveTime", "default");
cookie.setMaxAge(-1);
resp.addCookie(cookie);
}
protected void deleteNow(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = CookieUtils.findCookie("key_2", req.getCookies());
if (cookie != null) {
cookie.setMaxAge(0);
resp.addCookie(cookie);
resp.getWriter().write("key_2 的 Cookie 已经删除");
}
}
protected void liveTime(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("liveTime", "live3600");
cookie.setMaxAge(3600); // 3600 秒后删除
resp.addCookie(cookie);
resp.getWriter().write("已经创建存活一小时的 Cookie");
}
}
6、Cookie 的 path 属性
Cookie 的 path 属性,可以有效地过滤哪些 Cookie 可以发送给服务器,哪些不发。path 属性是通过请求的地址来进行有效的过滤。
(1)举个例子
假设有两个 Cookie:
- CookieA:path = /工程名
- CookieB:path = /工程名/file
(1-1)此时请求地址如下:
- http://localhost:8080/工程路径/post.html
- 那么,CookieA 可以发送到服务器,CookieB 不能发送到服务器。
(1-2)此时请求地址如下:
- http://localhost:8080/工程路径/file/post.html
- 那么,CookieA 和 CookieB 都可以发送到服务器。
(2)结论
显然只要 path 是请求地址的本目录以及父目录,就可以发送 Cookie 到服务器。
也就是说,请求地址 /post.html,不能发送 path 为 /file 的 Cookie;请求地址 /file/post.html,可以发送 path 为 /file 的 Cookie。
用更容易理解的来说,就是 path 的层级越小(越接近 /工程路径),那么就越容易发送到服务器。同时也意味着,请求地址的层级越大,越容易接收到 Cookie。
(3)示例
(3-1)Java 代码:
package com.web;
import com.utils.CookieUtils;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class CookieServlet extends BaseServlet {
protected void cookiePath(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("path_1", "path_1");
cookie.setPath(req.getContextPath() + "/file");
resp.addCookie(cookie);
resp.getWriter().write("创建了一个带有path的Cookie");
}
}
(3-2)结果:
(3-2-1)请求地址没有深入到 /工程路径/file:
此时第一次访问服务器,服务器为浏览器设置了 cookie:
即便设置了 Cookie,但是第二次访问服务器,并不能发送 Cookie 到服务器:
(3-2-2)请求地址深入到 /工程路径/file:
紧接着将 /file 加入请求地址,访问服务器(观察请求标头,此时包含有path):
访问后,会发现服务器已经接收到了请求标头中的 Cookie:
7、练习之免用户名登录
(1)示例:
(1-1)LoginServlet 类:
package com.web;
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 java.io.IOException;
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
if (username.equals("admin") && password.equals("password")) {
// 登陆成功
Cookie cookie = new Cookie("username", username);
// 一定要设置存活时间,否则关闭浏览器,cookie就销毁了
cookie.setMaxAge(7 * 24 * 60 * 60); // 一周内有效
resp.addCookie(cookie);
System.out.println("登陆成功");
} else {
System.out.println("登陆失败");
}
}
}
(1-2)login.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/cookie_war_exploded/start_LoginServlet" method="get">
用户名:<input type="text" name="username" value="${ cookie.username.value }"/> <br/>
密码:<input type="password" name="password"> <br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
(1-3)第一次输入正确的用户名和密码后,关闭该页面,重新访问,就会发现用户名已经自动填写上了:
cookie 中也确实存在该键值对
二、Session 简介
1、什么是 Session 对话
- Session 是一个 Java 中的接口(HttpSession)。
- Session 就是会话,用来维护一个客户端和服务器之间关联的一种技术。
- 每个客户端都有一个自己的 Session 会话。
- Session 会话中,我们经常用来保存用户登录后的信息,前面的 Cookie 是保存在客户端,而 Session 是保存在服务器端。
2、Session 的创建和获取
(1)创建和获取 Session,都是通过 request.getSession() 来实现的。
- 第一次调用:创建 Session 对象;
- 之后的调用:获取之前创建好的 Session 会话对象。
(2)可以用 isNew() 方法来判断一个 Session 是不是刚创建出来的。
- true:表示刚创建
- false:表示获取之前创建
(3)每个会话都有一个身份证号,也就是 ID 值,且这个 ID 唯一。
- getId() 方法获取 Session 对象的 ID 值
(4)示例:
注意前面 Cookie 第 2 小节的 BaseServlet 还要用。
(4-1)SesisonServlet 类:
package com.web;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class SessionServlet extends BaseServlet {
protected void createOrGetSession(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建和获取 session 会话对象
HttpSession httpSession = req.getSession();
// 判断是否新创建的 session
boolean isNew = httpSession.isNew();
// 获取 id
String id = httpSession.getId();
resp.getWriter().write("得到的 session 的 id 为:" + id + "\n");
resp.getWriter().write("得到的 session 是新创建的:" + isNew + "\n");
}
}
(4-2)session.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Session</title>
<style type="text/css">
ul li {
list-style: none;
}
</style>
<!-- 这里的工程名是 cookie,同前面第一节的工程,因为懒没有新建 session 工程 -->
<base href = "http://localhost:8080/cookie_war_exploded/"/>
</head>
<body>
<iframe name="target" width="500" height="500" style="float: left;"></iframe>
<div style="float: left;">
<ul>
<li><a href="./start_SessionServlet?action=createOrGetSession" target="target">Session的创建和获取(id号、是否为新创建)</a></li>
<li><a href="" target="target">Session域数据的存储</a></li>
<li><a href="" target="target">Session域数据的获取</a></li>
<li>Session的存活</li>
<li>
<ul>
<li><a href="" target="target">Session的默认超时及配置</a></li>
<li><a href="" target="target">Session3秒超时销毁</a></li>
<li><a href="" target="target">Session马上销毁</a></li>
</ul>
</li>
<li><a href="" target="target">浏览器和Session绑定的原理</a></li>
</ul>
</div>
</body>
</html>
(4-3)第一次点击创建,为 true:
(4-4)第二次点击创建,为 false:
3、Session 域中数据的存取
从 request 域中可以获取到 session 对象,然后调用 setAttribute 或者 getAttribute 即可。
(1)示例:
(1-1)SessionServlet 类:
package com.web;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class SessionServlet extends BaseServlet {
protected void setSessionAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().setAttribute("key_1", "value_1");
resp.getWriter().write("往 session 中保存了 key_1");
}
protected void getSessionAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object attribute = req.getSession().getAttribute("key_1");
resp.getWriter().write("从 session 中获取 key_1 的值为:" + attribute);
}
}
(1-2)结果:
4、Session 生命周期控制
Session 超时:客户端两次请求的最大时间间隔。
(1)setMaxInactiveInterval(int Interval)
设置 Session 的超时时间(以 秒 为单位),超过指定的时长,Session 就会被销毁。
- 值为正数:设置超时时长;
- 值为负数:设置永不超时(极少使用)
不能设置为 0;
(2)getMaxInactiveInterval()
获取 Session 的超时时长。
(3)invalidate()
让当前 Session 对话马上超时。
(4)示例:
(4-1)获取默认存活时间;
Session 默认超时时长为 30 分钟。因为在 Tomcat 服务器的配置文件 web.xml 中有以下配置,使得 web 工程下的所有 session 默认超时时长都为 30 分钟:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
如果要修改,可以在自己的 Web 工程中的 web.xml 中添加类似配置,不需要到 tomcat 的 xml 中修改。
(4-2)设置超时时长
使用 setMaxInactiveInterval() 可以单独为某个 Session 设置超时时长。
比如我们点击设置 3 秒超时,当我们点击创建一个 Session 后,再次点击,会发现 isNew 返回 false 对吧?
此时只需要等待 3 秒(显然等不够 3 秒就点,会重置超时时长),再次点击创建,就会发现 isNew 返回 true 了。
(4-3)设置 Session 直接超时无效
设置直接超时后,又点击创建,就会发现 isNew 也返回 true 了。
(4-4)SessionServlet 类:
package com.web;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class SessionServlet extends BaseServlet {
protected void deleteNow(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取了 Session 的默认超时时长
req.getSession().invalidate();
resp.getWriter().write("设置 Session 直接超时\n");
}
protected void threeSecondLiveTime(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取了 Session 的默认超时时长
req.getSession().setMaxInactiveInterval(3);
resp.getWriter().write("设置 Session 的存活时长为:" + 3 + " 秒\n");
}
protected void defaultLiveTime(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取了 Session 的默认超时时长
int maxInactive = req.getSession().getMaxInactiveInterval();
resp.getWriter().write("Session 的默认超时时长为:" + maxInactive + " 秒\n");
}
}
5、浏览器 Cookie 和服务器 Session 之间的关联
Session 技术的底层,其实是基于 Cookie 技术来实现的。