发展史
关于 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 中的格式解析成 键值对. |
getSession()方法:
既能用于获取到服务器上的会话,也能用于创建会话。
如果参数为 true:会话不存在,则创建会话;会话存在,则获取。(第一次登录的时候)
如果参数为 false:会话不存在,则返回 null;会话存在,则获取。(判断登陆状态的时候)
在调用 getSession 的时候需要做的事情:
- 创建会话
- 首先先获取到请求中
cookie
里面的sessionId
字段,相当于会话的身份标识 - 判定这个
sessionId
是否在当前服务器上存在。 - 如果不存在,则进入创建会话逻辑:
会创建一个HttpSession
对象,并且生成一个sessionId
(是一串很长的十六进制数,保证唯一性)
接下来就会把这个sessionId
作为key
,把这个HttpSession
对象作为value
,把这个键值对保存到服务器内存的一个“哈希表”这样的结构中。
最后,服务器就会返回一个 HTTP 响应,把sessionId
通过Set-Cookie
字段返回给浏览器。浏览器就可以保存这个sessionId
到 Cookie 中了。
- 获取会话
- 先获取到请求中的
cookie
里面的sessionId
字段(也就是会话的身份标识) - 判定这个
sessionId
是否在当前服务器上存在(也就是在这个"哈希表"中是否有) - 如果有,就直接查询出这个
HttpSession
对象,并且通过返回值返回回去。
getCookies()方法:
获取到请求中的 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
:存键值对。
Cookie 类中的相关方法
方法 | 描述 |
---|---|
String getName() | 该方法返回 cookie 的名称。名称在创建后不能改 变。(这个值是 Set-Cooke 字段设置给浏览器的) |
String getValue() | 该方法获取与 cookie 关联的值 |
void setValue(String newValue) | 该方法设置与 cookie 关联的值。 |
补充用法注意事项:
Cookie 这里是可以保存任意的键值对,包括用户自定义的键值对,所以在获取键值对时,如果是一般的键值对,直接通过 getCookies
来获取。如果是特殊的键值对(保存 sessionId
的键值对),直接用 getSession
来自动的帮我们从 Cookie
中取 sessionId
了。
实现登录案例交互:
图例分析:
约定前后端交互接口:
- 登录 + 获取主页
- 编写一个简单的登陆页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录系统</title>
<script src="js/jquery-1.9.1.min.js"></script>
</head>
<body>
<div style="margin-top:50px;margin-left:40%;">
<h1 style="padding-left:50px;">用户登录</h1>
姓名:<input id="username" type="text" >
<p>
密码:<input id="password" type="password" >
<p>
<div style="padding-left:50px;">
<!-- 提交事件-->
<input type="button" value=" 提 交 " onclick="mySubmit()">
<input type="reset" value=" 重 置 ">
</div>
</div>
<script>
// ajax 请求登录
function mySubmit(){
// 非空效验
var username = jQuery("#username");
var password = jQuery("#password");
if(""==jQuery.trim(username.val())){
alert("请先输入用户名!");
username.focus();
return;
}
if(""==jQuery.trim(password.val())){
alert("请先输入密码!");
password.focus();
return;
}
jQuery.ajax({
url:"login", // 设置请求地址
type:"POST", // 设置请求方法类型
contentType:"application/x-www-form-urlencoded; charset=utf-8", // 请求类型
// dataType:"", // 响应的类型
data:{"username":username.val(),"password":password.val()}, // 请求参数
success:function(data){
alert(data);
}
});
}
</script>
</body>
</html>
- 登录逻辑
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("<h3>欢迎你! " + username + " 这是第 " + count + " 次访问主页 </h3>");
}
}
Servlet 内部会帮我们去 维护这样相同的 sessionId 对应的对象,我们只需要根据业务需要进行对应的传参就好了。
交互成功~