🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!欢迎志同道合的朋友一起加油喔🤺🤺🤺
目录
一、Cookie和Session(面试常考)
1. Cookie
2. Session
3. Cookie和Session是如何一起工作的?
4. Cookie和Session的过期校验
5. Cookie和Session的关联和区别
二、核心方法
HttpServletRequest 类中的相关方法:
session对象中的常用方法
HttpServletResponse 类中的相关方法:
getCookies()方法
Cookie 类中相关的方法:
Cookie和Session方法使用的总结
三、实现一个用户登录的案例
案例登录淘宝 (演示前后端交互过程) 编辑
前后端交互中约定的内容
简单实现一个登录
约定前后端的交互接口
1. 代码实现
使用 form表单来构造 post 请求:
LoginServlet 类 (用来构造 POST响应)
IndexServlet 类 (用来构造跳转页面)
2. 代码分析
详解 LoginServlet 类 第1步代码
详解 IndexServlet 类 第1步代码
3. 理解代码交互的过程
4. 展示抓包结果
第一次交互
第二次交互
浏览器展示结果
在开发web应用的过程中,我们经常需要处理客户端和服务器之间的数据交换,以及跟踪用户在应用中的行为。在这里,我将向大家介绍两种重要的技术:Cookie和Session,它们在处理这些问题上起着关键作用。
一、Cookie和Session(面试常考)
1. Cookie
- Cookie是一种浏览器(客户端)提供的持久化存储数据的机制
(1) Cookie从哪里来?是客户端发送请求自带的吗?
- Cookie中的数据是来自于服务器的,服务器向浏览器发送 HTTP 响应时,它可以在响应头中包含一个或多个
Set-Cookie
头部来设置 Cookie,每一个Set-Cookie
头部都包含了一个 Cookie 的名称、值以及其它一些属性(如域名、路径、过期时间等)。
(2) Cookie 有什么用? Cookie要到哪里去?
- Cookie 会在第一次请求的下次请求中,自动被添加到 HTTP请求中的请求头,发给服务器。服务器通过 Cookie 中的内容,以此来验证用户的身份。
(3) Cookie是在哪里存的?
- Cookie存在浏览器所在主机的硬盘中,Cookie在存的时候是按照 浏览器 + 域名(地址)来进行细分的,不同的浏览器各自存各自的Cookie,同一个浏览器不同的域名对应不同的Cookie,Cookie里的内容不光是键值对还有过期时间
(4) Cookie保存的数据是什么格式?
- 多组键值对(键=值,多个键值对用分号间隔)(key = value;)
2. Session
- Session是一种服务端保存会话的技术,一次会话指登陆没有注销或者超时
由于HTTP协议是无状态的,所谓无状态,就是一次请求,一次响应,服务端无法感知之前登陆的用户,所以在服务端使用Map<String,Session>的数据结构来保存用户信息
3. Cookie和Session是如何一起工作的?
以登陆功能举例:
- 服务端校验账号密码成功后,生成一个随机字符串(sessionId用来标识用户身份)及一个Session对象(标识用户的该次对话),把sessionId作为键,Session对象作为值存入Map<String,Session>如果需要保存用户信息就保存在Session对象Map<String,Object>中,相当于登陆时服务端使用Session保存用户信息
- 登陆响应,服务端返回给客户端的HTTP响应数据包中,Set-Cookie响应头包含sessionId=xxx (相当于给客户端返回了一个身份id)
- 客户端收到响应后,保存Cookie信息,将响应的Set-Cookie中的内容保存在客户端本地(和此次服务器地址绑定)
- 客户端每次请求时,都携带sessionId=xxx在Cookie头中 (相当于客户端访问带着身份id访问)
- 服务端获取客户端请求时,先获取Cookie请求头中的内容,查找sessionId对应的值,然后从保存的Map结构中查找,如果存在就是登录用户,如果为null就是未登录 (服务器通过身份id确定客户端的身份,将保存的用户信息返回给客户端)
4. Cookie和Session的过期校验
🌵Session的过期校验
- 服务端保存的Session信息有默认的过期时间(可通过程序设置)
- 服务器有Session的过期校验机制:通过单独的线程扫描,发现当前时间和Session最后一次使用的时间超时就删掉
- 服务器存放Session的地方,web服务器默认是存放在内存中,所以重启服务器Session也就没了,但是有些服务器把数据保存在服务器硬盘,重启就还有
- 如果用户注销登录,相当于服务端删除Map中的Session
- 所以超时后,注销后,重启服务器后需要访问页面就需要重新登陆
🌳Cookie的过期校验
- Cookie也有过期时间(可以通过程序设置)
- 如果Cookie过期,浏览器发请求时就不会携带这些信息,服务端验证sessionId时就会验证失败,也就是没有登陆
- 如果在客户端手动删除Cookie,就相当于服务端还有Session信息,但是客户端请求时也不会携带Cookie信息,服务端验证sessionId失败,也就意味没有登陆
5. Cookie和Session的关联和区别
关联:
- 在网站的登陆功能中,需要配合使用.
区别:
- Cookie是客户端的存储机制. Session是服务器的存储机制.
- Cookie 里面可以存各种键值对(还可以存别的).Session 则专门用来保存用户的身份信息.
- Cookie 完全可以单独使用, 不搭配 session (实现非 登陆 场景下)
- Session 也可以不搭配 Cookie 使用. (手机 app 登陆服务器, 服务器也需要 Session, 此时就没有Cookie的概念) Cookie跟浏览器强相关的~~
- Cookie 是属于 HTTP 协议中的一个部分
- Session 则可以和HTTP 无关(TCP, websocket .. 也可以用 session)
注意:
- 此处 Cookie 和 Session 之间的配合,主要是针对一些主流的网页实现。Cookie 字段存在的意义就是:为了浏览器能够安全可靠地访问服务器的存储信息。在很多情况下,服务器会生成一个唯一的Session ID,并将其存储在一个Cookie中发送给浏览器。这样,当浏览器再次向服务器发送请求时,服务器可以通过读取Session ID来查找对应的Session
二、核心方法
HttpServletRequest 类中的相关方法:
方法 | 描述 |
---|---|
HttpSession getSession() | 在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null |
Cookie[] getCookies() | 返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把Cookie 中的格式解析成键值对. |
getSession 方法:既能用于获取到服务器上的会话,也能用于创建会话。具体行为取决于参数:
- 如果参数为true:若会话不存在,则创建;若会话存在,则获取
- 如果参数为false:若会话不存在,则返回null;若会话存在,则获取
调用getSession()的时候具体要做的事情:(默认参数为true,没有会话会自动创建会话)
- 创建会话
- 首先会从request对象中获取Cookie 存的 sessionId字段(相当于会话的身份标识),判定这个 sessionId 是否在当前服务器上存在,如果不存在,就会进入到创建会话的逻辑⬇️⬇️⬇️
创建会话: 就会创建一个HttpSession 对象 ,并且生成一个 sessionId(这个sessionId 是一个很长的数字,通常是用十六进制来表示,能够保证唯一性),接下来就会把这个 sessionId 作为 key,把这个Httpsession 对象作为value,把这个键值对给保存到服务器内存的一个"哈希表"(也不一定是他,只是类似这种结构)这样的结构中,再然后服务器就会返回一个HTTP 响应,把 sessionId 通过 Set-Cookie 字段返回给浏览器,浏览器就可以保存这个 sessionId 到 Cookie 中了。
- 获取会话
- 首先会从request对象中获取Cookie 存的 sessionId字段会话的身份标识),判定这个sessionId 是否在当前服务器上存在(也就是在哈希表中是否存在),如果有就直接查询出这个 HttpSession 对象,并且通过返回值返回回去。
那么HttpSession 对象到底是什么呢❓🤔
这个对象本质上也是一个“键值对”的结构,允许程序猿往HttpSession 对象(value)中,存储任意的键值对数据——key必须是String,value是一个Object对象(value部分才是真正存储的数据,这些数据可以是任何类型的Java对象) ,你可以通过相应的键来获取它们。
图解如下:
session对象中的常用方法
- public Object getAttribute(String name): 返回会话中与给定名字(键)关联的对象(值)。如果会话中没有给定名字的属性,返回null。
- public Enumeration<String> getAttributeNames(): 返回一个包含会话中所有属性名(即所有的键)的Enumeration对象。
- public void setAttribute(String name, Object value): 将一个对象(值)绑定到会话中的给定名字(键)。如果会话中已经有给定名字的对象,这个方法将替换掉原来的对象。
- public void removeAttribute(String name): 从会话中移除给定名字的属性。实际上就是删除会话中的一个键值对。
- public void invalidate(): 使会话无效,然后解绑它所有的对象。这一般用在用户登出时,使得当前的会话失效。
- public boolean isNew(): 如果客户端还不知道会话,或者如果客户端选择不加入会话,返回true。例如,如果服务器仅用cookie来维持会话,那么如果客户端禁用了cookie,这个方法将返回true。
- public void setMaxInactiveInterval(int interval): 设置会话在客户端没有交互的情况下的最大闲置时间(以秒为单位)。超过这个时间,服务器将使会话无效。
- public int getMaxInactiveInterval(): 返回会话在客户端没有交互的情况下的最大闲置时间(以秒为单位)。
- public long getCreationTime(): 返回会话被创建的时间(以1970年1月1日午夜(GMT)以来的毫秒数)。
- public long getLastAccessedTime(): 返回客户端最后一次请求会话所属的servlet的时间(以1970年1月1日午夜(GMT)以来的毫秒数)。
- public String getId(): 返回一个包含分配给会话的唯一标识符的字符串。这是会话的唯一ID,服务器用它来区分不同的会话。
HttpServletResponse 类中的相关方法:
方法 | 描述 |
void addCookie(Cookie cookie) | 添加指定的Cookie到响应头。这将使得浏览器在后续的请求中带上该Cookie |
void sendError(int sc) | 发送一个错误响应到客户端,使用指定的状态码 |
void sendError(int sc, String msg) | 发送一个错误响应到客户端,使用指定的状态码和详细消息 |
void sendRedirect(String location) | 将客户端重定向到新的位置(URL) |
void setStatus(int sc) | 设置响应的状态码 |
void setHeader(String name, String value) | 设置响应的头部信息 |
void setContentType(String type) | 设置响应的内容类型。例如,可以设置为"text/html"或者"application/json" |
PrintWriter getWriter() | 返回一个PrintWriter 对象,你可以使用它来向客户端发送文本数据 |
void setContentLength(int len) | 设置响应的内容长度。通常,Servlet容器会自动设置这个值,你不需要手动设置 |
void setCharacterEncoding(String charset) | 设置响应的字符编码 |
getCookies()方法
- 获取到请求中的Cookie数据,返回值是Cookie类型的数组,每个元素是一个Cookie对象,每个Cookie对象又包含了两个属性,name和value(还是键值对形式😂)
HTTP 请求中的 Cookie 字段就是按照键值对的方式来组织的,这里的这些键值对,大概的格式是使用 ; 来分割键值对,使用 = 来分割键和值,这些键值对都会在请求中通过 Cookie 字段传给服务器,服务器收到请求后,就会进行解析,解析成 Cookie[] 这样的形式。
由于Cookie这里是可以保存任意自定制的键值对,所以如果是一般的键值对,直接通过getCookies来获取;如果是特殊的键值对(表示sessionId的键值对),不需要使用getCookies,直接使用getSession其实就自动帮我们从Cookie中取sessionId了。
Cookie 类中相关的方法:
每个 Cookie 对象就是一个键值对
方法 | 描述 |
---|---|
String getName() | 该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 SetCooke 字段设置给浏览器的) |
String getValue() | 该方法获取与 cookie 关联的值 |
void setValue(String newValue) | 该方法设置与 cookie 关联的值 |
Cookie和Session方法使用的总结
-
Cookie和Session的作用:Cookie和Session主要用于跟踪用户的状态。由于HTTP协议本身是无状态的,即服务器默认每次请求都是独立的,因此为了跟踪用户的行为,需要引入Session和Cookie这样的机制。
-
Cookie的基本运作:服务器在响应一个请求时,可以设置一个或多个Cookie,并在HTTP响应头中发送给客户端。客户端会将Cookie保存起来,并在后续的请求中将这些Cookie包含在HTTP请求头中发送给服务器。
-
Session的基本运作:当用户首次访问一个应用时,服务器会为这个用户创建一个Session,并生成一个唯一的Session ID。这个Session ID会被存储在Cookie中并发送给客户端。后续的请求中,客户端会将这个Session ID包含在Cookie中一并发送给服务器,服务器便可根据这个Session ID获取到对应的Session。
- Session的使用:当服务器创建了一个会话(Session),它可以将任意数据存储在这个会话中。这些数据被存储为键值对(key-value pairs)。每个键值对都代表了一种信息,例如用户的身份信息、购物车信息等。键(key)是唯一的,可以用来获取或者修改对应的值(value)。
- 以Java Servlet为例,你可以这样操作Session:
// 获取与请求相关联的当前会话对象
HttpSession session = request.getSession();
// 将会话中key为 "username" 的值改 key为 "Tom" 的值
session.setAttribute("username", "Tom");
// 在会话中设置key为 "cart" 所对应的value值为 cartObj 对象
session.setAttribute("cart", cartObj);
上述代码首先获取了当前请求的Session对象,然后在Session中设置了用户名和购物车对象。这些信息将在整个Session生命周期内保持,除非被删除或修改。
- 获取和返回Session信息:在处理客户端的请求时,服务器可以根据需要获取Session中的信息。服务器代码负责获取这些信息,以及决定哪些信息将被返回给客户端。
- 依然以Java Servlet为例,你可以像下面这样获取Session中的信息:
// 获取与请求相关联的当前会话对象
HttpSession session = request.getSession();
// 从会话中获取key为 "username" 所对应的value值,并将其强制转换为字符串类型
String username = (String) session.getAttribute("username");
// 从会话中获取key为"cart" 所对应的value值,并将其强制转换为 Cart 对象类型
Cart cart = (Cart) session.getAttribute("cart");
上述代码首先获取了当前请求的Session对象,然后从Session中分别取出了用户名和购物车对象。你可以根据这些信息来处理请求,例如,你可以检查用户名是否存在,以确认用户是否已经登录;你也可以获取购物车对象,以响应用户对购物车的查询请求。
对于哪些信息应该返回给客户端,这完全取决于你的业务逻辑。例如,如果用户请求查看购物车,那么你可能需要将购物车对象转换为JSON,然后返回给客户端。
-
Session的生命周期:Session在一段时间内没有被使用(例如用户没有发出新的请求)后,会被服务器自动销毁。这个时间间隔可以由开发者设置。此外,服务器也可以在任何时候手动销毁一个Session。当Session被销毁后,相应的用户信息会被清除,用户需要重新登录或者重新设置个人信息等。
三、实现一个用户登录的案例
案例登录淘宝 (演示前后端交互过程)
服务器以session会话(键值对形式)保存每个用户的信息,键是表示当前用户身份的唯一序号(sessionId),值存储的是用户的信息。后续当浏览器带着唯一身份序号(sessionId)访问时,服务器在会话中按照sessionId获取到对应的session,再从session中按照预先设定的key(key是由程序员在编写代码时定义的。这个键用于标识存储在
HttpSession
对象中的某个特定的数据)获取用户信息,然后根据这些信息来处理用户的请求,并返回请求的结果给浏览器。
前后端交互中约定的内容
在前后端交互中,通常需要约定以下内容以确保系统的顺利运作:
-
请求类型:即 HTTP 方法(如 GET、POST、PUT、DELETE 等)。
-
请求 URL:即应用程序的端点(Endpoint),它指明了请求应该发送到哪里。
-
请求参数:如果请求中需要包含数据,需要约定使用 Query 参数、Path(URL) 参数,还是 Body 参数,以及这些参数的名称和意义。
-
请求头(Request Headers):可能需要约定一些特定的请求头,如
Content-Type
,Authorization
等。 -
请求体(Request Body):对于 POST、PUT 等方法,请求体中通常包含了要发送到服务器的数据。需要约定数据的格式(如 JSON、XML 等)和具体的数据结构。
-
响应状态码(Response Status Codes):需要约定各种情况下服务器会返回什么样的 HTTP 状态码。
-
响应体(Response Body):需要约定服务器返回的数据的格式和具体结构。
-
错误处理:需要约定在发生错误时,服务器会返回怎样的错误信息结构,以及前端应如何处理。
-
认证和授权:如何处理用户的身份验证和授权,例如使用什么样的认证机制(如 JWT)。
这些都是一般的约定,实际情况可能会根据具体的业务需求和技术选型有所不同。最重要的是,前后端开发团队之间需要有充分的沟通,以确保每一方都清楚地理解了约定内容。
简单实现一个登录
- 首先,要有一个 html,包含用户名密码的输入框,以及登录按钮。
- 其次,要有一个 LoginServlet,来处理登录请求。
- 最后,要有一个 IndexServlet,模拟登录完成后,跳转到的主页,在这个主页里面就能够获取到当前用户的身份信息。 ( 这里就可以存储开发人员自定义的用户数据,比如可以存一个当前用户访问的次数 )
约定前后端的交互接口
我们这里需要两组交互:一个是登录,另一个是获取主页。而针对前后端交互接口的话,实际上有很多种约定方式,我们选择下面这种来进行约定
1. 代码实现
之前说了一些理论的交互流程,而 Servlet 已经对这里的流程进行了封装,开发人员在使用的时候,相对简单,因为大部分工作,已经在 Servlet 的内部封装好了,只需要通过 一些API 进行调用即可。
使用 form表单来构造 post 请求:
<!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>
LoginServlet 类 (用来构造 POST响应)
@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");
//验证用户名密码是否正确
//正确情况下,用户名和密码是用数据库来保存
//此处直接写死
//此处约定, 用户名合法的是 zhangsan 和 lisi
//密码合法的都是 123
/*//这个代码固然能写出效果,但是嵌套太深了,还是等价转换一下
if(username.equals("zhangsan") || username.equals("lisi")) {
if(password.equals("123")) {
//登录成功
} else {
//登录失败
}
} else {
//登录失败
}*/
if(!username.equals("zhangsan") && !username.equals("lisi")) {
//登录失败 -> 重定向到 登录页面
System.out.println("登录失败,用户名错误");
resp.sendRedirect("login.html");
return;
}
if(!password.equals("123")) {
//登录失败 -> 重定向到 登录页面
System.out.println("登录失败,m密码错误");
resp.sendRedirect("login.html");
return;
}
//登录成功
//1. 创建一个会话
HttpSession session = req.getSession(true);
//2. 把当前的用户名保存到会话中
session.setAttribute("username",username);
//3. 重定向到主页
resp.sendRedirect("index");
}
}
IndexServlet 类 (用来构造跳转页面)
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
//通过重定向,浏览器发送的是 GET.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//先判断用户的登录状态
//如果用户还没登录,要求先登录
//已经登录了,根据 会话 中的用户名,来显示到页面上
//1.这个操作不会触发会话的创建
HttpSession session = req.getSession(false);
if(session == null) {
//未登录状态
System.out.println("用户未登录!");
resp.sendRedirect("login.html");
return;
}
//2.已经登录
String username = (String) session.getAttribute("username");
//3.构造页面
resp.setContentType("text/html;charSet=utf8");
resp.getWriter().write("欢迎" + username + "回来!");
}
}
2. 代码分析
详解 LoginServlet 类 第1步代码
HttpSession session = req.getSession(true);
上面的代码表示:session 这个会话在第一次登录成功之前,并没有创建,由于 getSession 方法的参数是 true,所以登录成功后,【不存在会话,就创建。】即创建了一个 session 对象,同时生成了一个 sessionID.
只不过,我们在代码中看不到生成的 sessionID 是什么,这是因为 Servlet 已经为我们封装好了代码。
详解 IndexServlet 类 第1步代码
HttpSession session = req.getSession(false);
上面的代码表示:通过 session 对象这个会话机制,来判定 session 对象有没有被创建出来,以此来判定用户是否已经登录过页面。
由于 getSession 方法中的参数是 false,【不存在,不创建】。既然不创建,也就只能用来判断用户是否已然是登录状态了。( 在 LoginServlet 类中,我们可以看到:只要用户名或密码输入错误,也就意味着登录失败,代码的逻辑是什么也不做,最终也就返回了,故而,session 这个对象就没有被创建出来,显然,用户没有成功登录。)
如果登录成功,说明 session 对象早已经被创建了,那么它的里面也就存储了键值对结构,包括用户名和密码
后续,如果我们通过值来找到对象,或者通过对象找值,也就很方便了。总之,我们就将其看作键值对的结构即可。
注意:
上面的两个不同类中的 session 表示的是同一个对象,也就是说,session 只有一份,我们可以将它想象成一个存钱罐,【存钱的时候,就往里面放对象;取钱的时候,就从里面拿对象。】
3. 理解代码交互的过程
我们将 Cookie 想象成一个身份令牌,同一个账户只有唯一一个令牌,每个令牌对应的sessionID 不相同,所以每个账户的令牌都不相同。那么,每个账户对应的 Cookie 也不相同,这也就实现了网页登录的安全性。此外,在第一次登录之后,客户端发送第二次请求的时候,就会自动带上 Cookie,这样一来,服务器就能直接通过 Cookie 中的 sessionID 就行识别,之后的操作,服务器就只对当前用户进行数据访问了。
4. 展示抓包结果
第一次交互:
第二次交互:
浏览器展示结果
在浏览器为我们呈现的结果就是,当我们输入正确的用户名和密码后,服务器端就会响应,并且跳转到另一个页面。这和我们平时通过网页登录就很相似了,当然,这里,我并没有将前端页面设置的很好看,重在表达逻辑。文章来源:https://www.toymoban.com/news/detail-465648.html
文章来源地址https://www.toymoban.com/news/detail-465648.html
到了这里,关于【Cookie和Session】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!