目录
二. 会话机制Session
三. 一些常用的核心方法及原理应用
1. HttpServletRequest 类中的相关方法
2. HttpServletResponse 类中的相关方法
3. HttpSession 类中的相关方法
4. Cookie 类中的相关方法
四. 代码案例:实现用户登录
1. 登录页面
2. LoginServlet 处理登录请求
3. IndexServlet 生成主页
4. 关注交互过程
一. 回顾cookie
在前面的这篇文章中:(1条消息) HTTP协议&协议报文结构&请求响应数据报分析_PlLI-的博客-CSDN博客
对cookie进行了介绍。
总结来说:
1.cookie是浏览器提供的持久化存储数据的机制;
2.cookie是从服务器在 Http响应 中通过 set-cookie 字段返回给客户端的,服务器代码中由程序员决定要把什么样的信息保存到客户端中;
3.cookie会在后续再次访问服务器的时候,在Http请求的 head部分中,再把cookie带回服务器中去。(此处的作用类似于上下文, 让服务器来识别客户端此时的状态)
4.cookie是存储在浏览器(客户端)所在的硬盘中的。
在这篇文章中,再对 cookie和session之间的交互关系进行讲解,并做出案例分析。
二. 会话机制Session
服务器在同一时刻收到的请求是很多的,这也就意味着服务器是需要同一时刻去处理很多不同的请求,因此服务器就需要去区分出每个请求是属于哪个客户端的,并对其状态进行分析,然后在服务器中对每个不同的客户端进行记录。而对客户端进行记录的这个机制,就是会话机制Session。
会话本质上一个键值对,通过一个哈希表来组织的,哈希表存储了一些键值对结构,key 存储的是 sessionId,每个不同用户的sessionId是不同的,具有唯一性,value 存储的就是这个用户的信息,此处的value是可以根据需求来进行灵活设计的。
1. 当用户登陆的时候, 服务器在 Session 中新增一个新记录,并把 sessionId 返回给客户端。 (例如通过 HTTP 响应中的 Set-Cookie 字段返回)。
2. 客户端后续再给服务器发送请求的时候,需要在请求中带上 sessionId (例如通过 HTTP 请求中的 Cookie 字段带上)。
3. 服务器收到请求之后, 根据请求中的 sessionId 在 Session 信息中获取到对应的用户信息,再进行后续操作,如果获取不到,就进行重新登陆。
而这就涉及到了 cookie 的一个最典型的应用了。
1. cookie:标识用户的身份信息
cookie中,可以存储 sessionId,用来标识不同用户之间的不同身份信息。
以登录功能为例进行讲解:
在登录的时候,服务器会根据客户端的身份信息,来分配一个唯一的 身份序号 sessionId,用来标识身份信息。同时服务器会通过 hash表这样的结构,把身份序号作为key,身份信息作为value,存储起来。并通过 Http响应中 set-cookie 字段,来把 sessionId传递给用户,然后用户在后续的访问中,都会带上这个 sessionId 来发送 Http请求,以便于服务器进行识别判定。当下一次请求的时候,服务器会识别 sessionId,在对应的hash表中进行查询,如果在hash表中查到了,也就知道了用户是谁了,也就不再需要重新登录,如果没查到,那就需要进行再一次登录。
2. cookie 和 session 的关联区别
关联:也就是上述登录过程的交互过程。
区别:
1. Cookie 是客户端的存储机制;session 是服务器的存储机制;
2. Cookie 里面可以存储各种键值对,并不只是存储 sessionId,而 session 则专门用来存储用户的身份信息;
其中Cookie也是可以单独使用的,不搭配session;例如:在实现非登录的场景下;
session 也可以不搭配 Cookie使用;例如:手机app登录访问服务器,此时服务器也需要 session,但就没有了Cookie的概念,此时 sessionId就不再需要通过 cookie或者set-cookie进行传递了。Cookie是跟浏览器强相关的;
Cookie 是属于HTTP协议中的一个部分;
session则可以和HTTP无关,在 TCP等其他地方也可以用到 session;
此处再进行一次补充:所谓的会话,是一个键值对,key是sessionId,value是一个HttpSession对象。
三. 一些常用的核心方法及原理应用
1. HttpServletRequest 类中的相关方法
HttpSession getSession() |
在服务器中获取会话。参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null;
|
Cookie[]
getCookies()
|
返回一个数组,
包含客户端发送该请求的所有的
Cookie
对象。
会自动把Cookie 中的格式解析成键值对。
|
注意此处的 getSession() 方法既可以用于获取服务器中的会话,也可以用于创建会话。取决于参数的值,返回会话中的value,也就是 HttpSession。
例如对于请求语句:(req 为 HttpServletRequest 的一个实例)
此处的参数为 true,此时服务器就会判定当前用户是否已经有了对应的会话了(拿着请求中 cookie 里的 sessionId 查一下哈希表) 如果 sessionId 不存在,或者没查到,就创建新会话,并插入到哈希表中。如果查到了,直接返回查到的结果。
会话的创建过程:
1. 创建新的 HttpSession 对象;
2. 构造唯一的 sessionId;
3. 把这个键值对插入哈希表;
4. 把这个 sessionId 设置到响应报文的 Set-cookie 字段中;
注意:HttpSession 对象也是一个键值对。存储这用户的身份信息。 可以通过setAttribute() 和 getAttribute() 来存取键值对,此处的键值对也是由程序员自定义的数据。
用图来说明服务器中会话是如何组织的:
2. HttpServletResponse 类中的相关方法
void addCookie(Cookie cookie)
|
把指定的
cookie
添加到响应中。
|
Cookie cookie = new Cookie("name",""heihei);
resp.addCookie(cookie);
从抓包结果就可以观察:
3. HttpSession 类中的相关方法
每个HttpSession 对象也是通过键值对的形式来存储数据的。 一个 HttpSession 对象里面包含多个键值对。我们可以往 HttpSession 中存任何我们需要的信息。
当要获取数据时,使用方法 getAttribute(String name) 来获取,返回键值对中 key为 name 的对应value 值;
当存储数据时,使用方法 setAttribute(String name, Object value) 来进行存储,name 对应键值对中的 key,value 对应 键值对中的 value;
Object getAttribute(String name)
|
该方法返回在该
session
会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null.
|
void setAttribute(String name, Object value)
|
该方法使用指定的名称绑定一个对象到该
session
会话
|
boolean isNew()
|
判定当前是否是新创建出的会话
|
4. Cookie 类中的相关方法
每个 Cookie 对象就是一个键值对
String getName()
|
该方法返回
cookie
的名称。名称在创建后不能改变。
(这个值是 Set-Cooke 字段设置给浏览器的)
|
String getValue()
|
该方法获取与
cookie
关联的值。
|
void setValue(String newValue)
|
该方法设置与
cookie
关联的值。
|
四. 代码案例:实现用户登录
1. 登录页面
登录页面使用form表单来进行构造Http请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
</head>
<body>
<form action="login" method="post">
<input type="text" name="username">
<br>
<input type="password" name="password">
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
抓包分析:
2. LoginServlet 处理登录请求
正常情况下,验证用户密码正不正确是从数据库进行判断,此处为了简单,将用户固定为 zhangsan或者lisi,密码为123。
package login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
if (!username.equals("zhangsan") && !username.equals("lisi")){
// 登陆失败!!
// 重定向到 登陆页面
System.out.println("登录失败,用户名错误!");
resp.sendRedirect("login.html");
return;
}
if (!password.equals("123")){
// 登陆失败!!
System.out.println("登录失败,密码错误!");
resp.sendRedirect("login.html");
return;
}
// 登陆成功
// 1. 创建一个会话.
// 参数为 true:判定当前请求是否已有对应的会话,也就是拿着请求中的 cookie 里的 sessionId 查一下哈希表,如果有,直接返回已有的,没有则创建
HttpSession session = req.getSession(true);
// 2. 把当前的用户名和对应值构造成键值对,保存到会话中的 HttpSession. 此处 HttpSession 又可以当成一个 map 使用.
session.setAttribute("username",username);
// 3. 重定向到主页
resp.sendRedirect("index");
}
}
抓包分析:
3. IndexServlet 生成主页
package login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
// 通过 重定向, 浏览器发起的是 GET .
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 先判定用户的登陆状态.
// 如果用户还没登陆, 要求先登陆.
// 已经登陆了, 则根据 会话 中的用户名, 来显示到页面上.
// 这个操作不会触发会话的创建.
// 此处参数为 false:判定当前客户端是否已有对应的会话,也就是拿着请求中的 cookie 里的 sessionId 查一下哈希表,如果有,直接返回已有的,没有则不会创建
HttpSession session = req.getSession(false);
if (session == null){
// 未登录状态
System.out.println("用户未登录!");
resp.sendRedirect("login.html");
return;
}
// 已经登陆
String username = (String)session.getAttribute("username");
resp.setContentType("text/html;charset=utf-8");
// 构造页面
resp.getWriter().write("欢迎 " + username + "回来!");
}
}
4. 关注交互过程