目录
1.Cookie概念
2.理解会话机制 (Session)
3.相关API
3.1HttpServletRequest
3.2HttpServletResponse
3.3HttpSession
3.4Cookie
4.代码示例: 实现用户登陆
1.Cookie概念
Cookie 是存储在用户本地终端(如计算机、手机等)上的数据片段。
它主要有以下一些特点和作用:
特点:
小型数据存储:通常用于存储少量的信息。
与特定网站关联:由访问的网站创建和管理。
作用:
用户识别:帮助网站识别用户身份,实现登录状态保持等功能。
个性化设置保存:比如记住用户的偏好设置。
跟踪用户行为:用于分析和优化网站体验。
然而,Cookie 也存在一些隐私方面的问题,可能会被用于跟踪用户的浏览行为等。用户可以在浏览器中对 Cookie 的使用进行一定程度的管理和控制。
在实际开发中,我们很多时候是需要知道请求之间的关联关系的
例如登陆网站成功后, 第二次访问的时候服务器就能知道该请求是否是已经登陆过了.
图中的 "令牌" 通常就存储在 Cookie 字段中
1. 例如到了医院先挂号. 挂号时候需要提供身份证, 同时得到了一张 "就诊卡", 这个就诊卡就相当于患者的 "令牌".
2. 后续去各个科室进行检查, 诊断, 开药等操作, 都不必再出示身份证了, 只要凭就诊卡即可识别出当前患者的身份.
3. 看完病了之后, 不想要就诊卡了, 就可以注销这个卡. 此时患者的身份和就诊卡的关联就销毁了. (类似于网站的注销操作)
4. 又来看病, 可以办一张新的就诊卡, 此时就得到了一个新的 "令牌"
此时在服务器这边需要记录令牌信息, 以及令牌对应的用户信息, 这就是 Session 机制所做的工作
2.理解会话机制 (Session)
服务器同一时刻收到的请求是很多的. 服务器需要清除的区分清楚每个请求是从属于哪个用户, 就需要在服务器这边记录每个用户令牌以及用户的信息的对应关系.
在上面的例子中, 就诊卡就是一张 "令牌". 要想让这个令牌能够生效, 就需要医院这边通过系统记录 每个就诊卡和患者信息之间的关联关系.
会话的本质就是一个 "哈希表", 存储了一些键值对结构. key 就是令牌的 ID(token/sessionId), value 就是 用户信息(用户信息可以根据需求灵活设计).
sessionId 是由服务器生成的一个 "唯一性字符串", 从 session 机制的角度来看, 这个唯一性字符串称为 "sessionId"..在整个登录流程中看待, 也可以把这个唯一性字符串称为 "token".
当用户登陆的时候, 服务器在 Session 中新增一个新记录, 并把 sessionId / token 返回给客户端. (例如通过 HTTP 响应中的 Set-Cookie 字段返回).
客户端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId/ token. (例如通过 HTTP 请求中的 Cookie 字段带上).
服务器收到请求之后, 根据请求中的 sessionId / token 在 Session 信息中获取到对应的用户信息,再进行后续操作.
Servlet 的 Session 默认是保存在内存中的. 如果重启服务器则 Session 数据就会丢失.
3.相关API
3.1HttpServletRequest
方法 | 描述 |
HttpSession getSession() | 在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果为false, 则当不存在会话时返回 null |
Cookie[] getCookies() | 返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把 Cookie 中的格式解析成键值对. |
3.2HttpServletResponse
方法 | 描述 |
void addCookie(Cookie cookie) | 把指定的 cookie 添加到响应中. |
3.3HttpSession
一个 HttpSession 对象里面包含多个键值对. 我们可以往 HttpSession 中存任何我们需要的信息
方法 | 描述 |
Object getAttribute(String name) | 该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null. |
void setAttribute(String name, Object value) | 该方法使用指定的名称绑定一个对象到该 session 会话 |
boolean isNew() | 判定当前是否是新创建出的会话 |
3.4Cookie
每个 Cookie 对象就是一个键值对.
方法 | 描述 |
String getName() | 该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 Set- Cooke 字段设置给浏览器的) |
String getValue() | 该方法获取与 cookie 关联的值 |
void setValue(String newValue) | 该方法设置与 cookie 关联的值。 |
HTTP的Cooke 字段中存储的是多组键值对. 每个键值对在 Servlet 中都对应了一个 Cookie 对象
通过 HttpServletRequest.getCookies() 获取到请求中的一系列 Cookie 键值对.
通过 HttpServletResponse.addCookie() 可以向响应中添加新的 Cookie 键值对.
4.代码示例: 实现用户登陆
实现简单的用户登陆逻辑,代码主要是通过HttpSession类完成. 不需要我们手动操作 Cookie对象.
1. 创建 IndexServlet 类
@WebServlet("/index")
public class IndexServlet extends HelloServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
// 1. 判定当前用户是否已经登陆
HttpSession session = req.getSession(false);
if (session == null) {
// 用户没有登陆 , 重定向到 login.html
resp.sendRedirect("login.html");
return;
}
// 2. 如果已经登陆 , 则从 Session 中取出访问次数数据
String userName = (String) session.getAttribute("username");
String countString = (String) session.getAttribute("loginCount");
int loginCount = Integer.parseInt(countString);
loginCount += 1;
session.setAttribute("loginCount", loginCount + "");
// 3. 展示到页面上 .
StringBuilder html = new StringBuilder();
html.append(String.format("<div>用户名: %s</div>", userName));
html.append(String.format("<div>loginCount: %d</div>", loginCount));
resp.getWriter().write(html.toString());
}
}
2. 创建 login.html, 放到 webapp 目录中
<form action="login" method="POST">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="提交">
</form>
3. 创建 LoginServlet 类
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
// 1. 获取到用户提交的用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
// 2. 判定用户名密码是否正确
if (!username.equals("admin") || !password.equals("123")) {
// 登陆失败
resp.getWriter().write("登陆失败");
return;
}
// 登陆成功
System.out.println("登陆成功");
// 设置 Session
HttpSession session = req.getSession(true);
session.setAttribute("username", "admin");
session.setAttribute("loginCount", "0");
resp.sendRedirect("index");
}
}
此处的 getSession 参数为 true, 表示查找不到 HttpSession 时会创建新的 HttpSession 对象, 并生成一个 sessionId, 插入到哈希表中, 并且把 sessionId 通过 Set-Cookie 返回给浏览器.
4. 部署程序
通过 http://127.0.0.1:8080/ServletHelloWorld/index 访问.
首次访问的时候可以看到, 当前用户尚未登陆, 此时页面自动重定向到 login.html
抓包结果显示:
在 login.html 中输入用户名密码之后, 会跳转到 /login 路径. 此时服务器返回了一个 token, 并在 Session 中记录用户信息, 然后重定向到 /index
抓包结果:
注意: 响应中的 302表示重定向,其中JSESSIONID为 Servlet 自动生成的 token
在/index中, 通过 Session 拿到了用户信息, 进一步获取到用户的访问次数.