Cookie 与 Session
一、Cookie 详解
1.Cookie 是什么?
Cookie
,是某些网站为了辨别用户身份,进行session
跟踪而储存在用户本地终端上的数据,由用户客户端计算机暂时或永久保存的信息;
2.为什么使用Cookie?
由于
web
程序是使用HTTP
传输协议的,而HTTP
协议属于无状态的协议,对于事务处理没有记忆能力;因此当后续处理需要前面的信息时就需要重新上传,这样就会导致每次连接传送的数据量增大了;
Cookie
的出现就可以解决这一问题:
原理:
第一次登录后服务器返回一些数据(cookie)
给浏览器,然后浏览器将其保存在本地,当该用户发送第二次请求时 就会把上次请求存储的cookie
数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是那个了;
简单来讲cookie
的工作原理就是:
给每个用户发放一个通行证,这样服务器就能通过通行证确认用户身份了;
3.Cookie 产生时间
Cookie
的使用首先取决于用户(原因:浏览器可以禁用Cookie
,同时服务端也可以不set-cookie
;Cookie
的两种保存方式:其1为浏览器将Cookie保存在内存中,其2为保存在客户端的硬盘上
,之后每次HTTP
请求浏览器都会将Cookie
发送给服务端;
4.Cookie 生存周期
Cookie
在生成时就会被指定一个Expire
值,这就是Cookie
的生存周期,在这个周期内Cookie
有效,超出周期就会被清除;
5.Cookie 缺陷
数量受限制
表现在:
一个浏览器能创建的
Cookie
数量最多不超过300
个,且每个不能超过4KB
,每个web
站点能设置的Cookie
总数不能超过20
个;
安全性得不到保障
:
表现在:存在跨站点脚本攻击
浏览器可以设置禁用Cookie
:
6.Cookie 使用场景
基于Cookie
的缺陷,因此一般使用在:
- 对安全性要求不高;
- 不需要存储大量的数据;
- 主要用于客户端与服务器之间的状态保持;
二、Session 详解
1.什么是 Session
Session
也称为会话机制,那什么是会话呢?
会话的本质:就是一个"哈希表", 存储了一些键值对结构,其中key
就是令牌的ID
(token/sessionId
),value
就是用户信息(用户信息可以根据需求灵活设计);
sessionId 和 token 就可以理解成是同一个东西的不同叫法;
流程为:
- 当用户登陆的时候, 服务器在
Session
中新增一个新记录, 并把sessionId / token
返回给客户端(例如通过HTTP
响应中的Set-Cookie
字段返回); - 客户端后续再给服务器发送请求的时候, 需要在请求中带上
sessionId/ token
(例如通过HTTP
请求中的Cookie
字段带上); - 服务器收到请求之后, 根据请求中的
sessionId / token
在Session
信息中获取到对应的用户信息, 再进行后续操作;
2.Session 生命周期
根据需求设定,一般为半个小时
(当你登陆一个服务器时,服务器返回给你一个sessionID
,登陆成功后的半个小时之内没有对该服务器进行任何的HTTP
请求,半个小时之后进行一次HTTP
请求,就会提示你重新登陆);
三、涉及核心方法
HttpServletRequest
类中的相关方法:
方法 | 描述 |
---|---|
HttpSession getSession() | 在服务器中获取会话,参数为true ,当会话不存在时,新建会话;参数为false 时,会话不存在返回null |
Cookie[] getCookies() | 返回一个数组,包含客户端发送请求的所有Cookie 对象,会自动把Cookie 中的格式解析成键值对 |
HttpServletResponse
类中的相关方法:
方法 | 描述 |
---|---|
void addCookie(Cookie cookie) | 把指定的Cookie 添加到响应中 |
HttpSession
类中的相关方法:
方法 | 描述 |
---|---|
Object getAttribute(String name) | 该方法返回在该 session 会话中具有指定名称的对象,没有返回null |
void setAttribute(String name, Object value) | 该方法使用指定的名称绑定一个对象到该 session 会话 |
boolean isNew() | 判定当前是否是新创建出的会话 |
Cookie
类中的相关方法:
方法 | 描述 |
---|---|
String getName() | 返回 cookie 的名称 ,名称在创建后不能改变,(这个值是 Set-Cooke 字段设置给浏览器的) |
String getValue() | 获取与 cookie 关联的值 |
void setValue(String newValue) | 设置与 cookie 关联的值 |
四、代码示例(用户登陆)
简单实现用户登陆,登陆成功后允许访问敏感资源
:
前端代码:请求方法为 post
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>用户登陆</h3>
<form action="login" method="post">
<input type="text" name="username" placeholder="请输入用户名">
<br>
<input type="text" name="password" placeholder="请输入密码">
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
后端代码:
- 创建一个
LoginServlet
package session;
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 {
//body:username=xxx&password=xxx
//登陆成功:跳转到request.html,此时允许访问敏感资源(/sensitive)
//登陆失败:返回错误信息,此时不允许访问敏感资源
String username = req.getParameter("username");
String password = req.getParameter("password");
//模拟数据库进行账号密码登陆
if("abc".equals(username) && "123".equals(password)){
//登陆成功
//为登陆成功的用户创建一个session
//tomcat启动时维护了一个数据结Map<String,Session>
//用来保存多个用户的会话信息,键为sessionID的值(随机字符串)
//获取当前请求的session,如果没有获取到就重新创建一个放在Map结构中
HttpSession session =req.getSession(true);
//session对象本身也是一个Map结构,可以保存多组键值对的信息
session.setAttribute("u","username");
//以上代码会设置一个set-cookie,jsessionid=随机字符串
//跳转到登录后的页面
resp.sendRedirect("request.html");
}else{
//登陆失败,返回错误信息
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().println("登陆失败");
}
}
}
- 创建一个
SensitiveServlet
package session;
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("/sensitive")
public class SensitiveServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//获取当次请求的session(此时可能登陆,也可能没有登陆)
//如果没有获取到,就返回null
HttpSession session = req.getSession(false);
if(session != null){
//session 不为空
String username = (String) session.getAttribute("u");
if(username != null){
//session设置的数据也不为空
resp.getWriter().println("用户已登陆,允许访问");
return;
}
}
resp.getWriter().println("用户未登录,不允许访问");
}
}
运行 tomcat
,三种情况如下所示:
- (1)当没有登陆时就访问
sensitive
,此时出现如下界面所示内容:
- (2)当访问登陆页面时,输入错误的账号密码时,如下所示:
当点击提交时,也面会发生跳转,但会显示如下登陆失败页面:
- (3)只有输入正确的账号密码时,页面才会跳转到
request.html
中,显示:
服务器会给当前用户设置一个set-cookie
,jsessionid
=随机字符串:
五、小结
Cookie
是客户端的机制,它可以存储在浏览器,也可以存储在本地;Session
是服务器端的机制,只能存储在服务器;session
能够存储任意的Java
对象,但cookie
只能存储string
类型的对象;session
占用服务器性能,session
越多就会增加服务器压力;cookie
没有session
安全性高;- 单个
cookie
保存的数据不能超过4KB
,而session
的大小限制与服务器本身的内存大小相关;