🐲 一文搞懂 Session Cookie

一、理论基础

1、会话控制核心操作(重中之重)

  • 会话域写操作:session.setAttribute(字符串类型的属性名, Object类型的属性值);

  • 会话域读操作:Object value = session.getAttribute(字符串类型的属性名);

  • 想要在一个会话的范围内保持某个状态,就把数据存入会话域即可

  • 例如:用户登录就是把用户对象保存到会话域,代表用户已登录这个状态

  • 我们常用的是HttpSession来保持会话状态,但Session是基于Cookie生效的

HTTP协议本身是『无状态』的,怎么理解这个『无状态』呢?就是说:HTTP协议本身是不存储用户状态的,每个请求都是独立的。这样一来,HTTP协议就只负责定义请求、响应的报文的格式即可。

  • 好处:HTTP协议本身更简单,基于HTTP协议设计项目架构更简单
  • 坏处:为了保持会话状态,需要额外另想办法

2、Cookie的工作机制

①设定场景

咖啡店发布了一个促销活动:消费五杯咖啡,赠送一杯。

又不能要求用户一次性消费完这五杯,用户肯定是不同的日期来店里消费,累积够五杯之后,再赠送。

可是店员不认识顾客,不能单凭顾客的一面之词就相信他消费了五杯。

所以咖啡店设计了一个卡片,顾客每消费一杯,就在卡片上盖一个印章。

集齐五个印章,赠送一杯。

本质上这里解决的是识别用户身份的问题。

对于我们服务器端程序,也有相同的问题:如何识别上一个、下一个请求是来自同一个浏览器。

②Cookie工作机制要点

  • Cookie数据是在服务器端生成的
  • 服务器端生成Cookie之后通过响应消息头返回给浏览器
  • 浏览器保存Cookie信息
  • 浏览器以后访问服务器都会自动携带Cookie
  • 服务器端可以从请求对象中读取Cookie信息

③操作:服务器创建并返回Cookie

java 复制代码
// 1、创建 Cookie 对象  
Cookie cookie = new Cookie("CookieName01", "CookieValue01");  
  
// 2、把 Cookie 对象添加到响应中  
// 底层会在响应消息头中附加下面内容:  
// Set-Cookie: CookieName01=CookieValue01  
response.addCookie(cookie);  
  
// 3、返回响应  
response.setContentType("text/html;charset=UTF-8");  
response.getWriter().write("success");

响应消息头截图:

④观察:浏览器再次访问服务器自动携带Cookie

⑤操作:服务器端读取Cookie信息

java 复制代码
response.setContentType("text/html;charset=UTF-8");  
PrintWriter writer = response.getWriter();  
  
// 1、通过 request 对象获取 Cookie 数组  
// ※注意:这个 Cookie 数组不一定有值,只有浏览器发送请求时携带的 Cookie 才有  
Cookie[] cookies = request.getCookies();  
  
// 2、对 Cookie 数组做一个判空保护  
if (cookies != null && cookies.length > 0) {  
  
    // 3、遍历 Cookie 数组  
    for (int i = 0; i < cookies.length; i++) {  
        Cookie cookie = cookies[i];  
        writer.write("Cookie Name:" + cookie.getName());  
        writer.write("<br/>");  
        writer.write("Cookie Value:" + cookie.getValue());  
    }  
} else {  
    writer.write("没有获取到任何Cookie信息。");  
}

⑥理论:Cookie时效性

[1]从时效性角度Cookie分成两种

  • 会话级:保存在浏览器的内存中,只要浏览器开着就一直在。浏览器关闭的时候释放。这是默认情况。
  • 持久化:保存在浏览器的硬盘中,此时必然是服务器端设定的过期时间。到过期时间时被释放。

[2]setMaxAge()方法

  • expire参数是正数情况:设置 Cookie 以秒为单位作为过期时间
  • expire参数是0情况:告诉浏览器删除这个 Cookie
  • expire参数是负数情况:把 Cookie 设置为会话级
java 复制代码
// 1、创建 Cookie 对象  
Cookie cookie = new Cookie("timedCookieName", "timedCookieValue");  
  
// 2、给 Cookie 设置过期时间  
// expire参数是正数情况:设置 Cookie 以秒为单位作为过期时间  
// expire参数是0情况:告诉浏览器删除这个 Cookie
// expire参数是负数情况:把 Cookie 设置为会话级  
cookie.setMaxAge(-10);  
  
// 3、在响应中添加 Cookieresponse.addCookie(cookie);  
  
// 4、返回响应  
response.setContentType("text/html;charset=UTF-8");  
response.getWriter().write("success");

⑦、Cookie的使用建议

  • Cookie有如下限制:
    • 单个Cookie能够保存的数据非常有限
    • 浏览器保存Cookie时,属于不同网站的Cookie的数量也是有限的
    • 来自于同一个网站的Cookie也只能保存有限的个数(约20个)
    • 用户随时可能删除浏览器端的所有Cookie
    • 用户也可以禁用浏览器端的Cookie
  • Cookie的开发建议
    • 不要在Cookie中保存太多数据
    • 同一个域名下,Cookie保存的数量不要太多
    • 不要过于依赖Cookie作为数据保存的手段
    • Cookie主要是用来保存那些能够识别用户身份的数据

3、Session的工作机制

①咖啡买五送一

参见PPT

②代码演示Session工作机制

调用 request.getSession(); 方法获取 HttpSession 对象时:

  • 当前请求中没有携带 JSESSIONID 的 Cookie
    • 服务器端创建新的 HttpSession 对象
    • 每一个 HttpSession 对象都有一个唯一标识:JSESSIONID
    • 基于 JSESSIONID 创建 Cookie
    • Cookie的名字是 "JSESSIONID"这个字符串,值就是 JESSIONID 对应的唯一值
    • 然后这个名叫 JSESSIONID 的 Cookie 会随着响应返回给浏览器
  • 当前请求中携带了 JSESSIONID 的 Cookie
    • 根据 JSESSIONID 在服务器端查找对应的 HttpSession 对象
      • 能找到:把找到的 HttpSession 对象作为 getSession() 方法的返回值
      • 找不到:服务器端创建新的 HttpSession 对象......
java 复制代码
response.setContentType("text/html;charset=UTF-8");  
PrintWriter writer = response.getWriter();  
  
// 1、尝试获取 HttpSession 对象  
HttpSession session = request.getSession();  
  
// 2、调用 HttpSession 对象的 isNew() 方法查看这个 HttpSession 对象是否为新创建的  
boolean isNew = session.isNew();  
writer.write(isNew?"当前HttpSession对象是新创建的":"当前HttpSession对象是旧的,以前创建的");  
writer.write("<br/>");  
// 3、获取当前 HttpSession 对象的 id 值  
String id = session.getId();  
writer.write("当前HttpSession对象的id=" + id);

③时效性

[1]介绍

  • 服务器端做 HttpSession 对象的管理有个目标:不能无限创建 HttpSession 对象。
  • 一旦浏览器端 JSESSIONID 的 Cookie 丢失,留在服务器端的 HttpSession 对象就再也用不上了。
  • 用不上的 HttpSession 对象留在服务器端就只是白白占用内存空间,时间长了会把内存耗尽。
  • 所以我们不允许 HttpSession 对象永远保存在服务器。
  • 具体做法是:一旦 HttpSession 对象空闲达到指定的时间,就会强行把它释放。
  • 在Tomcat的conf/web.xml中可以看到默认设置:
xml 复制代码
  <!-- ==================== Default Session Configuration ================= -->
  <!-- You can set the default session timeout (in minutes) for all newly   -->
  <!-- created sessions by modifying the value below.                       -->

    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>

[2]测试代码

java 复制代码
// 1、获取 HttpSession 对象  
HttpSession session = request.getSession();  
  
// 2、修改最大空闲时间  
// Inactive:不活跃的  
// Interval:时间间隔  
session.setMaxInactiveInterval(10);

[3]让Session立即失效

java 复制代码
session.invalidate();

使用场景举例:用户退出登录时,把HttpSession对象彻底干掉,整个会话域全部释放。

所以要结合业务需求考虑清楚,是"一锅端"还是"定点清除"。

  • 一锅端:session.invalidate();
  • 定点清除:session.removeAttribute("属性名");

以登录为例:

  • 登录:session.setAttribute("loginUser", user);
  • 退出登录:
    • 一锅端:session.invalidate();// Session域的其它属性也都没了
    • 定点清除:session.removeAttribute("loginUser");// Session域的其它属性都还在

4、到底啥叫会话?

二、登录功能

1、建模

①物理建模

sql 复制代码
create table t_user(  
    user_id int auto_increment primary key ,  
    user_name char(100),  
    user_pwd char(100),  
    user_nick_name char(100)  
);  
insert into t_user(user_name, user_pwd, user_nick_name) VALUES ("tom2023", "123456", "tom");

②逻辑建模

java 复制代码
public class User {  
  
    private Integer userId;  
    private String userName;  
    private String userPwd;  
    private String userNickName;

2、创建组件

  • UserDao
  • UserDaoImpl
  • UserService
  • UserServiceImpl
  • UserServlet
xml 复制代码
<servlet>  
    <servlet-name>UserServlet</servlet-name>  
    <servlet-class>com.demo.servlet.model.UserServlet</servlet-class>  
</servlet>  
<servlet-mapping>  
    <servlet-name>UserServlet</servlet-name>  
    <url-pattern>/UserServlet/*</url-pattern>  
</servlet-mapping>

3、显示登录页面

①编写超链接

html 复制代码
<a th:href="@{/UserServlet/toLoginPage}">登录</a>

②UserServlet的toLoginPage()方法

java 复制代码
protected void toLoginPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
    processTemplate("view05-login", request, response);  
}

③登录的表单页面

html 复制代码
<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>登录</title>  
</head>  
<body>  
  
    <form th:action="@{/UserServlet/login}" method="post">  
        <p th:text="${message}"></p>  
        账号:<input type="text" name="userName" /><br/>  
        密码:<input type="password" name="userPwd" /><br/>  
        <button type="submit">登录</button>  
    </form>  
  
</body>  
</html>

4、登录验证

①Servlet方法

java 复制代码
protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
    String userName = request.getParameter("userName");  
    String userPwd = request.getParameter("userPwd");  
  
    User user = userService.login(userName, userPwd);  
    if (user == null) {  
        request.setAttribute("message", "用户名、密码不正确!");  
        processTemplate("view05-login", request, response);  
    } else {  
        HttpSession session = request.getSession();  
        session.setAttribute("loginUser", user);  
        response.sendRedirect(request.getContextPath() + "/index.html");  
    }  
}

②Service方法

java 复制代码
@Override  
public User login(String userName, String userPwd) {  
  
    return userDao.selectUserForLogin(userName, userPwd);  
}

③Dao方法

java 复制代码
@Override  
public User selectUserForLogin(String userName, String userPwd) {  
    String sql = "select user_id userId, user_name userName, user_pwd userPwd, user_nick_name userNickName from t_user where user_name=? and user_pwd=?";  
    return getSingleBean(sql, User.class, userName, userPwd);  
}
相关推荐
等一场春雨11 分钟前
Java设计模式 八 适配器模式 (Adapter Pattern)
java·设计模式·适配器模式
一弓虽33 分钟前
java基础学习——jdbc基础知识详细介绍
java·学习·jdbc·连接池
王磊鑫33 分钟前
Java入门笔记(1)
java·开发语言·笔记
马剑威(威哥爱编程)1 小时前
2025春招 SpringCloud 面试题汇总
后端·spring·spring cloud
硬件人某某某1 小时前
Java基于SSM框架的社区团购系统小程序设计与实现(附源码,文档,部署)
java·开发语言·社区团购小程序·团购小程序·java社区团购小程序
程序员徐师兄1 小时前
Java 基于 SpringBoot 的校园外卖点餐平台微信小程序(附源码,部署,文档)
java·spring boot·微信小程序·校园外卖点餐·外卖点餐小程序·校园外卖点餐小程序
chengpei1471 小时前
chrome游览器JSON Formatter插件无效问题排查,FastJsonHttpMessageConverter导致Content-Type返回不正确
java·前端·chrome·spring boot·json
Quantum&Coder1 小时前
Objective-C语言的计算机基础
开发语言·后端·golang
五味香1 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
Joeysoda1 小时前
Java数据结构 (从0构建链表(LinkedList))
java·linux·开发语言·数据结构·windows·链表·1024程序员节