Cookie 和 Session、实现用户登录逻辑

news2024/11/17 12:50:37

Cookie 和 Session

  • 一、回忆 Cookie
  • 二、理解会话机制 (Session)
  • 三、Cookie 和 Session 的区别
  • 四、核心方法
    • 4.1 HttpServletRequest 类中的相关方法
    • 4.2 HttpServletResponse 类中的相关方法
    • 4.3 HttpSession 类中的相关方法
    • 4.4 Cookie 类中的相关方法
  • 五、代码示例: 实现用户登陆
    • 5.1 需求
    • 5.2 登录页面的实现
    • 5.3 跳转页面的实现

一、回忆 Cookie

在讲 HTTP 协议时,我们曾提到过 Cookie:https://blog.csdn.net/yyhgo_/article/details/128433565?spm=1001.2014.3001.5501

HTTP 协议自身是属于 “无状态” 协议。

“无状态” 的含义:默认情况下 HTTP 协议的客户端和服务器之间的这次通信,和下次通信之间没有直接的联系。

但是实际开发中,我们很多时候是需要知道请求之间的关联关系的

例如登陆网站成功后,第二次访问的时候服务器就能知道该请求是否是已经登陆过了。

Cookie 就是浏览器在本地存储数据的一种机制 (存到硬盘上)。

Cookie 中存储了一个字符串,这个数据可能是客户端 (网页) 自行通过 JS 写入的,也可能来自于服务器 (服务器在 HTTP 响应的 header 中通过 Set-Cookie 字段给浏览器返回数据)!

在这里插入图片描述
在这里插入图片描述

浏览器提供的持久化存储方案,有好几种:
Cookie是最经典的一种方案 (最老);
LocalStorage是比较新的一种方案;
indexDB是更新的方案…

查看Cookie:
在这里插入图片描述
在这里插入图片描述

使用cookie作为保存数据的手段,只能存一些简单的键值对信息,简单的字符串~~ 你想让它存个图片、视频、存个flash… 万万做不到!
比如,可以使用cookie存:1.上次访问页面的时间;2.当前网页的访问次数;3.当前访问页面的身份信息 (身份标识 id)… 都是程序员自己控制的 ~

  • Cookie从哪儿来?
    Cookie是存在浏览器的,来源是服务器。
    在这里插入图片描述
  • Cookie到哪儿去?
    在这里插入图片描述

Cookie典型的一个应用场景:在客户端维持登陆状态。
在某个网站上登陆成功后,浏览器就会记住当前登陆用户的身份信息,然后接下来访问网站的其他页面,服务器也能知道是谁在登陆。(总不能每次打开一个页面都重新登陆一下)
在这里插入图片描述
服务器要给很多客户端提供服务,每个客户端都对应一组键值对~~
很多网站都是基于这一套来实现的 (主流方式);也有一些使用localStorage等一些新的方法。
是不是非常类似于医院的就诊卡呢?在不同的科室这张卡都能标识身份信息~~

二、理解会话机制 (Session)

服务器同一时刻收到的请求是很多的。服务器需要清除的区分清楚每个请求是从属于哪个用户,就需要在服务器这边记录每个用户令牌以及用户的信息的对应关系。

在上面的例子中,就诊卡就是一张 “令牌”。要想让这个令牌能够生效,就需要医院这边通过系统记录每个就诊卡和患者信息之间的关联关系。

会话的本质就是一个 “哈希表”,存储了一些键值对结构。key 就是令牌的 ID(token/sessionId),value 就是用户信息 (用户信息可以根据需求灵活设计)。

sessionId 是由服务器生成的一个 “唯一性字符串”,从 session 机制的角度来看,这个唯一性字符串称为 “sessionId”。但是站在整个登录流程中看待,也可以把这个唯一性字符串称为 “token”。
sessionId 和 token 就可以理解成是同一个东西的不同叫法 (不同视角的叫法)。

在这里插入图片描述

  • 当用户登陆的时候,服务器在 Session 中新增一个新记录,并把 sessionId / token 返回给客户端 (例
    如通过 HTTP 响应中的 Set-Cookie 字段返回)。
  • 客户端后续再给服务器发送请求的时候,需要在请求中带上 sessionId/ token (例如通过 HTTP 请求
    中的 Cookie 字段带上)。
  • 服务器收到请求之后,根据请求中的 sessionId / token 在 Session 信息中获取到对应的用户信息,再进行后续操作。

在这里插入图片描述

Servlet 的 Session 默认是保存在内存中的,如果重启服务器则 Session 数据就会丢失!

三、Cookie 和 Session 的区别

Cookie 是客户端的机制;Session 是服务器端的机制。
Cookie 和 Session 经常会在一起配合使用 (并不是必须)。
完全可以用 Cookie 来保存一些数据在客户端,这些数据不一定是用户身份信息,也不一定是 token / sessionId;Session 中的 token / sessionId 也不需要非得通过 Cookie / Set-Cookie 传递 ~~

在这里插入图片描述

四、核心方法

在 Servlet 中也专门提供了相关的API,让我们来操作 Cookie 和 Session。

4.1 HttpServletRequest 类中的相关方法

方法描述
HttpSession getSession()在服务器中获取会话。参数如果为 true, 则当不存在会话时新建会话;参数如果为 false, 则当不存在会话时返回 null
Cookie[] getCookies()返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象。会自动把 Cookie 中的格式解析成键值对

getSession() 使用模式有两种:
1)参数填写 false:判定当前会话是否存在 (根据请求中的 Cookie 里的 sessionld 来查哈希表)。如果不存在直接返回 null;存在则返回对应的HttpSession对象。
2)参数填写true:判定当前会话是否存在 (根据请求中的 Cookie 里的 sessionld 来查哈希表)。如果不存在就创建一个新的键值对,保存到哈希表中,并把生成的 sessionld 返回到浏览器这里;如果存在则直接返回对应的 HttpSession 对象。

getCookies()
直接把请求中的 Cookie 都获取到。Cookie 本身也是键值对结构。
请求里的 Cookie 可能有多组键值对,就直接使用数组表示。

4.2 HttpServletResponse 类中的相关方法

方法描述
void addCookie(Cookie cookie)把指定的 cookie 添加到响应中

返回响应,你想给浏览器返回哪些 cookie 都可以通过这个方法来添加。
在这里添加的键值对,都会体现在 HTTP 响应报文的 Set-Cookie 字段上。

4.3 HttpSession 类中的相关方法

一个 HttpSession 对象里面包含多个键值对,我们可以往 HttpSession 中存任何我们需要的信息!

方法描述
Object getAttribute(String name)该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null
void setAttribute(String name, Object value)该方法使用指定的名称绑定一个对象到该 session 会话
boolean isNew()判定当前是否是新创建出的会话

4.4 Cookie 类中的相关方法

每个 Cookie 对象就是一个键值对。

方法描述
String getName()该方法返回 cookie 的名称。名称在创建后不能改变 (这个值是 Set-Cookie 字段设置给浏览器的)
String getValue()该方法获取与 cookie 关联的值
void setValue(String newValue)该方法设置与 cookie 关联的值
  • HTTP 的 Cookei 字段中存储的实际上是多组键值对,每个键值对在 Servlet 中都对应了一个 Cookie 对象
  • 通过 HttpServletRequest.getCookies() 获取到请求中的一系列 Cookie 键值对
  • 通过 HttpServletResponse.addCookie() 可以向响应中添加新的 Cookie 键值对

五、代码示例: 实现用户登陆

实现简单的用户登陆逻辑。

这个代码中主要是通过 HttpSession 类完成,并不需要我们手动操作 Cookie 对象。

5.1 需求

1)登陆页面。用户可以填写用户名 + 密码 (login.html)
2)Servlet 来处理登陆请求。(LoginServlet.java)
3)使用另一个 Servlet 来生成主页内容 (登陆成功后,跳转到的页面) (IndexServlet)

5.2 登录页面的实现

注意代码注释!

login.html:

<!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>
    <!-- 提交的数据交给 LoginServlet 来处理的 -->
    <form action="login" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="登陆">
    </form>
</body>
</html>

LoginServlet.java:

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("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 从请求中获取到页面提交的用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        // 2. 验证用户名密码是否正确
        //    正常这个操作要查数据库. 此处为了简单, 直接写成硬编码了. (把正确的用户名和密码写死了)
        //    假定正确的密码是 zhangsan 123
        if ("zhangsan".equals(username) && "123".equals(password)) {
            // 登陆成功
            // a) 创建一个会话. 用户刚登陆成功, 之前是没有会话的. 重新分配个新的会话给用户.
            // getSession
            //    * 创建 sessionId 和一个 HttpSession 对象
            //    * 把这两个内容以键值对的形式插入到内存的 哈希表 里
            //    * 把 sessionId 通过 Set-Cookie 写到响应中.
            HttpSession session = req.getSession(true);
            // 随意的设置 "键值对" 了. (HttpSession 对象自身也相当于是一个 哈希表)
            session.setAttribute("username", "zhangsan");
            // b) 让响应重定向到 "主页"
            resp.sendRedirect("index");
        } else {
            // 登陆失败
            resp.setStatus(403);
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("登陆失败, 用户名或者密码错误");
        }
    }
}

这里通过"zhangsan".equals(username)比较,而不是username.equals("zhangsan"),避免了空指针异常 ~~

  • 此处的 getSession 参数为 true,表示查找不到 HttpSession 时会创建新的 HttpSession 对象,并生成一个 sessionId,以键值对的形式插入到 哈希表 中,并且把 sessionId 通过 Set-Cookie 返回给浏览器。

在这里插入图片描述

通过 http://127.0.0.1:8080/yyhjava/login.html 访问页面:

在这里插入图片描述
登录"zhangsan","123"后跳转页面:

在这里插入图片描述

此时还并没有实现跳转页面,所以就是 404 ~~

在登录用户名为 “zhangsan” 时,使用 Fiddler 抓包:
在这里插入图片描述
第一个为由 form 表单发出的 POST 请求:
请求:
在这里插入图片描述
响应:
在这里插入图片描述
第二个为页面跳转的 GET 请求,此时为 404:
请求:
在这里插入图片描述
响应:
在这里插入图片描述

Cookie 和 Session 细节:

1)在登录页面的响应中,有这样一行:

在这里插入图片描述

这就是通过 getSession 操作创建会话的同时,生成 sessionld 并且把这个 sessionld 通过 Set-Cookie 返回给浏览器了!!!

会话的 sessionld 是一个键值对结构:JSESSIONID=82ABD3C7775819D26A7F6ECA46C1355A
Key:JSESSIONID (Servlet生成的名字);Value:一串十六进制数字
每个会话的sessionld都不同!!!

2)在页面跳转的请求中,有这样一行:
在这里插入图片描述

这就是刚才设置的 sessionld 啊 ~ 说明已经保存在浏览器中 (本地)了!

当然也可以在浏览器查看:(点击 URL 前的小图标 ~)

在这里插入图片描述

是不是很眼熟?当然一模一样 ~

由此总结:
HttpSession session = req.getSession(true);代码虽短,干的活却很多!
1)创建 sessionId 和一个 HttpSession 对象
2)把这两个内容以键值对的形式插入到内存的 哈希表
3)把 sessionId 通过 Set-Cookie 写到响应中

5.3 跳转页面的实现

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 {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 首页中也先获取 session, 此处的 session 正是刚才登陆页的时候, 登陆成功的逻辑中创建出来的.
        // 此处 参数 写作 false 即可. 表示不新建. 如果不存在, 就返回 null 就是了.
        HttpSession session = req.getSession(false);
        if (session == null) {
            resp.setStatus(403);
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("您尚未登录, 不能访问主页!");
            return;
        }

        String username = (String) session.getAttribute("username");

        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("欢迎来到主页! " + username);
    }
}
  • 在这个代码中是看不到 “哈希表”,也看不到 sessionId 这样的概念的。getSession 操作内部提取到请求中的 Cookie 里的 sessionId,然后查找哈希表,获取到对应的 HttpSession 对象。
  • getSession 参数为 false,则获取不到 HttpSession 对象,不会创建新的 HttpSession,而是返回 null。此时说明用户没有登陆。

在这里插入图片描述

保存了什么,才能取出什么 ~~

此处的会话是根据 sessionld 来查哈希表的。
因此只要把浏览器这里的 cookie 删了,不登录就直接访问 index 页面,就会触发这个 if 逻辑了:
在这里插入图片描述
这样删除:
在这里插入图片描述

要想触发这个 if 逻辑可以删掉 cookie,是不是也可以删掉服务器这里存储会话的哈希表?
这个哈希表是隐藏在 Servlet 背后的男人,没有 API 能直接访问!
有同学可能会想到:这个东西应该是作为内存中的变量,那么重启服务器 tomcat 不就把内存的数据给重置了嘛?!
不一定!这是 IDEA / smart tomcat 的锅 ~~
在这里插入图片描述
在开发阶段可以帮助我们调试代码的时候节省时间。
但在生产环境上部署所面临的情况,就是一般的 tomcat,这点一定要明确 ~~

此时就可以成功实现逻辑了!!!登录后:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/136421.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

转速传感器信号隔离变送模块PCB焊接方波信号输出

特点 转速传感器信号直接输入&#xff0c;方波信号输出正弦波、锯齿波信号输入&#xff0c;方波信号输出200mV峰值微弱信号的放大与整形不改变原波形频率&#xff0c;响应速度快电源、信号&#xff1a;输入/输出 3000VDC三隔离辅助电源&#xff1a;5V、12V、15V或24V直流单电源…

【学Vue就像玩一样】什么是计算属性?什么使监视属性?

目录 1.计算属性 2.监视属性 2.1监视属性 2.2深度监视 3.computed和watch的区别 本栏会细致的将Vue划分为两大模块&#xff0c;基础篇&#xff0c;进阶篇。想要顺利通关vue篇首先要拥有三剑客&#xff0c;当然node ajax法宝也会辅佐你通关的&#xff0c;在学习的途中如果遇…

【回答问题】ChatGPT上线了!给我推荐20个比较流行的自动驾驶算法模型

给我推荐20个比较流行的自动驾驶算法模型 当前比较流行的自动驾驶模型有&#xff1a; 卷积神经网络&#xff08;CNN&#xff09; 长短时记忆网络&#xff08;LSTM&#xff09; 自适应调和滤波器&#xff08;ADF&#xff09; 自适应估计滤波器&#xff08;AEF&#xff09; 线性…

Vue组件及组件之间的通信

目录 一、Vue脚手架的使用 1、通过命令行使用vue-cli的指令创建&#xff1a;vue init webpack 项目名称 2、使用webStorm软件&#xff1a;本质仍然使用vue脚手架 3、使用vue ui创建&#xff1a;vue提供的图形化的操作界面 二、Vue的组件和组件之间的通信 1、组件&#xff…

使用线性回归、LGBM对二手车价格进行预测

使用线性回归、LGBM对二手车价格进行预测 目录使用线性回归、LGBM对二手车价格进行预测说明数据导入、查看和清洗数据说明导入训练集导入测试集合并数据查看数据整体情况处理数据检查并处理缺失变量EDA年份和价格地区和价格前任里程和价格燃料类型和价格传动装置类型Mileage与价…

数据库 SQL 高级用法

目录 一、INSERT INTO SELECT 用法 二、逻辑控制语句 三、公式表表达式 四、存储程序 五、触发器 一、INSERT INTO SELECT 用法 INSERT INTO SELECT 语句从一个表复制数据&#xff0c;然后把数据插入到一个已存在的表中。 1、从一个表中复制所有的列插入到另一个已存在的表…

React(coderwhy)- 01

React的介绍&#xff08;技术角度&#xff09; React是什么&#xff1f;React&#xff1a;用于构建用户界面的 JavaScript 库React的官网文档&#xff1a;https://zh-hans.reactjs.org/React的特点&#xff1a; 声明式编程组件化开发多平台适配Hello React react需要3个依赖&am…

【数字图像处理】骨骼锐化

源码链接&#xff1a;skeleton.cpp 一、实验要求 附件是人体骨骼核扫描图像&#xff0c;我们的目的是通过图像锐化突出骨骼的更多细节来增强图像。图像灰度的动态范围很窄并且有很高的噪声内容。 二、实验内容 按照课本冈萨雷斯的《数字图像处理》上面的思路&#xff0c;整…

【代码题】五道链表面试题

目录 1.移除链表元素 2.反转链表 3.链表的中间结点 4.链表中倒数第k个结点 5.合并两个有序链表 1.移除链表元素 点击进入该题 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回新的头节点 。 思路&am…

CTF-AWD入门手册

引文 AWD赛制是一种网络安全竞赛的赛制。AWD赛制由安全竞赛专家及行业专家凭借十多年实战经验&#xff0c;将真实网络安全防护设备设施加入抽象的网络环境中&#xff0c;模拟政府、企业、院校等单位的典型网络结构和配置&#xff0c;开展的一种人人对抗的竞赛方式&#xff0c;…

语言和文法的形式定义---编译原理

文法的构建问题 * 参考已有的模型 最经典的即是算数表达式的模型&#xff0c;其有多个算术运算符号和优先级别。 文法与正则表达式与有穷自动机的转换 显然是3型文法&#xff0c;也就是正则文法才有相应的性质&#xff0c;因为只有3型文法才是右部至多仅有两个符 号&#xf…

【Vue路由】props配置、replace属性、编程式路由导航、缓存路由组件

文章目录props配置props值为对象props值为布尔值props值为函数总结\<router-link>的replace属性总结编程式路由导航案例实现总结缓存路由组件案例实现总结props配置 我们可以看看我们原来如何使用传递过来的参数的&#xff1a; 我们要写一大长串去从$route身上拿到我们…

传统目标跟踪——光流法

目录 一、光流法 二、LK光流法 2.1 实现原理 2.2 API 三、代码 四、总结 一、光流法 光流&#xff1a;空间运动物体在观察成像平面上像素运动的瞬时速度。 光流法利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧之间存在的对应关系&#xff0c;…

密码学_RSA

RSA是1977年由罗纳德李维斯特&#xff08;Ron Rivest&#xff09;、阿迪萨莫尔&#xff08;Adi Shamir&#xff09;和伦纳德阿德曼&#xff08;Leonard Adleman&#xff09;一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。 RSA是非…

dom截图的几种实现方式

前端要实现dom截图的功能&#xff0c;现在比较常用的是使用以下两个库 dom-to-image 使用svg技术实现html2canvas 使用canvas技术实现 如果想自己写一个dom截图的可以参考 dom-pointer 代码比较简单&#xff0c;非常适合拿来研究rasterizeHTML.js 目前rasterizeHTML.js已经被…

1231. 航班时间(恶心的输入处理 + 简单的数学)

题目如下&#xff1a; 题解 or 思路&#xff1a; 因为题目假设两次飞行时间是相同的&#xff0c;我们可以通过减法将时差消去。那么飞行时间就是: time1time22\frac{time_1 time2}{2}2time1​time2​ 题目的难点是处理输入&#xff0c;我们可以使用 sscanf 来进行处理&#x…

小程序开发经验分享(1)

账号搭建篇 1. 获取微信小程序的AppID 首先,我们需要拥有一个帐号,如果你能看到该文档,我们应当已经邀请并为你创建好一个帐号。注意不可直接使用服务号或订阅号的AppID。 利用提供的帐号,登录 https://mp.weixin.qq.com ,就可以在网站的“设置”-“开发者设置”中,查…

MySQL高级 SQL优化【插入数据主键优化】

目录 1&#xff1a;SQL优化 1.1&#xff1a;插入数据 1.1.1&#xff1a;insert 1). 优化方案一&#xff08;批量插入数据) 2). 优化方案二&#xff08;手动控制事务&#xff09; 3). 优化方案三 &#xff08;主键顺序插入&#xff0c;性能要高于乱序插入。&#xff09; …

java应用集成HanLP进行中文自然语言分词详细完整案例以及demo

本文可以作为上一篇《mysql/mariadb 实现全文检索》的补充&#xff0c;实现对字符串分词的逻辑 什么是自然语言&#xff0c;什么是自然语言分词及例子 什么是自然语言 狭义地讲&#xff0c;利用计算机进行语言分析的研究是一门语言学与计算机科学的交叉学科&#xff0c;学术界…

CTFSHOW新手杯MISC部分WriteUp

引文 之前复现了CTFSHOW新人杯的WEB方向部分题目&#xff0c;今天就复现一下MISC为主的题目&#xff0c;可能有些读者不太明白MISC方向是什么意思&#xff0c;简单来说就是"杂项",包括&#xff1a;隐写&#xff0c;压缩包处理&#xff0c;流量分析&#xff0c;攻击取…