关于 cookie、session与token的真正区别 引用:东北一绝、英俊侠作者的文章。
Cookie 和 Session 工作流程基于以上关于:在 HTTP 前后端交互中由于 HTTP 是 “无状态协议”,而在实际用户使用浏览器时不单单为了查询信息 / 资源,而是需要进行一些相关身份验证(登录状态),而又不能在每跳转到一个页面都要重新进行验证~ 于是在我们的服务器就需要储存的一些 信息 和 状态 的记录 而发展的 Cookie 和 Session 机制,对标于我们的一个具体的 Servlet 项目,以下是对相关 API 和 工作流程 的详细介绍。
"无状态" 的含义指的是: 默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系
在基于 Servlet 框架的使用 HTTP 进行前后端交互项目中,对于 Cookie 和 Session 都有很好的支持,可以基于相关类提供的 API 来对我们自己的项目进行会话管理的操作。
Servlet 提供的关键 API HttpServletRequest 类中的相关方法| 方法 | 描述 |
|---|---|
| HttpSession getSession() | 在服务器中获取会话. 参数如果为 true, 则当不存 在会话时新建会话; 参数如果为 false, 则当不存在 会话时返回 null |
| Cookie[] getCookies() | 返回⼀个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会⾃动把 Cookie 中的格式解析成 键值对. |
既能用于获取到服务器上的会话,也能用于创建会话。
如果参数为 true:会话不存在,则创建会话;会话存在,则获取。(第一次登录的时候)
如果参数为 false:会话不存在,则返回 null;会话存在,则获取。(判断登陆状态的时候)
在调用 getSession 的时候需要做的事情:
- 创建会话
- 首先先获取到请求中 cookie 里面的 sessionId字段,相当于会话的身份标识
- 判定这个 sessionId 是否在当前服务器上存在。
- 如果不存在,则进入创建会话逻辑:
会创建一个 HttpSession 对象,并且生成一个 sessionId(是一串很长的十六进制数,保证唯一性)
接下来就会把这个 sessionId 作为 key,把这个 HttpSession 对象作为 value,把这个键值对保存到服务器内存的一个“哈希表”这样的结构中。
最后,服务器就会返回一个 HTTP 响应,把 sessionId 通过 Set-Cookie 字段返回给浏览器。浏览器就可以保存这个 sessionId 到 Cookie 中了。
- 获取会话
- 先获取到请求中的 cookie 里面的 sessionId 字段(也就是会话的身份标识)
- 判定这个 sessionId 是否在当前服务器上存在(也就是在这个"哈希表"中是否有)
- 如果有,就直接查询出这个 HttpSession 对象,并且通过返回值返回回去。
获取到请求中的 Cookie 数据,返回值是 Cookie 类型的数组,每个元素是一个 Cookie 对象。每个 Cookie对象又包含了两个属性:name,value (还是键值对)。
HTTP 请求中的 cookie 字段就是按照键值对的方式来组织的,这些键值对都会在请求中通过 cookie 字段传给服务器,服务器收到请求之后,就会进行解析,解析成 Cookie[] 这样的形式来返回。
HttpServletResponse 类中的相关方法| 方法 | 描述 |
|---|---|
| void addCookie(Cookie cookie) | 把指定的 cookie 添加到响应中. |
响应中就可以根据 addCookie 这个方法来添加一个 Cookie 信息到响应报文中,这里添加进来的键值对,就会作为 Http 响应中的 Set-Cookie 字段来表示。
HttpSession 类中的相关方法| 方法 | 描述 |
|---|---|
| Object getAttribute(String name) | 该⽅法返回在该 session 会话中具有指定名称的 对象,如果没有指定名称的对象,则返回 null |
| void setAttribute(String name, Object value) | 该⽅法使⽤指定的名称绑定⼀个对象到该 session 会话 |
| boolean isNew() | 判定当前是否是新创建出的会话 |
- HttpSession
这个对象本质上也是一个“键值对”结构,允许程序员往 HttpSession 对象中储存任意的键值对结构(key 必须是 String,value 是一个 Object)
- HttpSession 对象里面包含了若干键值对,里面的 key 和 value 都是程序员自定义的。
- HttpSession 里面的每个键值对,称为 属性(Attribute).。
- HeepSession 提供了两个方法:
getAttribute :取键值对。
setAttribute :存键值对。
| 方法 | 描述 |
|---|---|
| String getName() | 该方法返回 cookie 的名称。名称在创建后不能改 变。(这个值是 Set-Cooke 字段设置给浏览器的) |
| String getValue() | 该方法获取与 cookie 关联的值 |
| void setValue(String newValue) | 该方法设置与 cookie 关联的值。 |
Cookie 这里是可以保存任意的键值对,包括用户自定义的键值对,所以在获取键值对时,如果是一般的键值对,直接通过 getCookies 来获取。如果是特殊的键值对(保存 sessionId 的键值对),直接用 getSession 来自动的帮我们从 Cookie 中取 sessionId 了。
实现登录案例交互:图例分析:
约定前后端交互接口:
- 登录 + 获取主页
- 编写一个简单的登陆页面:
用户登录系统
用户登录
姓名:
密码:
- 登录逻辑
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("/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");
// 判定用户名或者密码是否正确~~
// 正常来说这个判定操作是要放到数据库中进行存取的.
// 此处为了简单, 就直接在代码里写死了. 假设有效的用户名和密码是 "admin", "123"
if ("admin".equals(username) && "123".equals(password)) {
// 登录成功!
// 创建会话, 并保存必要的身份信息.
HttpSession httpSession = req.getSession(true);
// 往会话中存储键值对. 必要的身份信息
httpSession.setAttribute("username", username);
// 初始情况下, 把登录次数设为 0
httpSession.setAttribute("count", 0);
resp.sendRedirect("index");
} else {
// 登录失败!
resp.getWriter().println("login failed!");
}
}
}
- 主页逻辑
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 {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 返回一个主页. (主页就是一个简单的 html 片段)
// 此处需要得到用户名是啥, 从 HttpSession 中就能拿到.
// 此处 getSession 的参数必须是 false. 前面在登录过程中, 已经创建过会话了. 此处是要直接获取到之前的会话.
HttpSession session = req.getSession(false);
String username = (String) session.getAttribute("username");
// 还从会话中取出 count.
Integer count = (Integer) session.getAttribute("count");
count += 1;
// 把自增之后的值写回到会话中.
session.setAttribute("count", count);
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("欢迎你! " + username + " 这是第 " + count + " 次访问主页 ");
}
}
Servlet 内部会帮我们去 维护这样相同的 sessionId 对应的对象,我们只需要根据业务需要进行对应的传参就好了。
交互成功~



