Cookie 和 Session

1. 回顾

Cookie是浏览器在本地持久化存储结构的一种机制.

1.1 Cookie的数据从哪里来?

服务器返回给浏览器的.

1.2 Cookie的数据是什么样的?

Cookie的数据是键值对结构.

并且这里的键值对都是程序员自定义的.

1.3 Cookie的作用是什么?

Cookie可以在浏览器这边存储一些"临时性的数据".

其实最典型的一种使用方式,就是用来存储"身份标识".(sessionId)

这里涉及到 Cookie和Session之间的联动.

后续在访问该网站的其它页面的时候,请求中就会自动带上刚才的sessionId,进一步服务器就可以知道当前是哪个用户在操作了.

1.4 Cookie到哪里去?

Cookie的内容会在下次访问该网站的时候,自动的被带到HTTP请求中.

1.5 Cookie怎么存的?

浏览器按照不同的"域名",分别存储Cookie.

域名和域名之间的Cookie是不能干扰的.

Cookie存储在硬盘上,往往会有一个超时时间. 

2. Cookie操作

2.1 方法 

①HttpServletRequest 类中的相关方法:

方法描述
Cookie[] getCookies()返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把 Cookie 中的格式解析成键值对.

②HttpServletResponse 类中的相关方法:

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

把Cookie添加到响应中,在HTTP响应报文里,加了一个Set-Cookie header.


③Cookie 类中的相关方法:

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

方法描述
String getName()该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 Set Cooke 字段设置给浏览器的)
String getValue()该方法获取与 cookie 关联的值
void setValue(String newValue)该方法设置与 cookie 关联的值。

 

2.2 方法实现

@WebServlet("/setcookie")
public class SetCookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //期望通过这个doGet方法,把一个自定义的Cookie数据返回到浏览器这边

        Cookie cookie = new Cookie("date","2023-04-02");
        resp.addCookie(cookie);
        Cookie cookie2 = new Cookie("time","15:18");
        resp.addCookie(cookie2);
        resp.getWriter().write("setCookie ok");

    }
}
@WebServlet("/getcookie")
public class GetCookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取到这次请求的cookie
        Cookie[] cookies = req.getCookies();
        if (cookies != null){
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + ": " + cookie.getValue());
            }
        }else {
            System.out.println("请求中没有cookie");
        }

        resp.getWriter().write("ok");
    }

}

①首先,发送getcookie请求:

 

此时并没有Cookie,自然getcookie请求里面也没有.

②下面发起setcookie请求:

请求这里并没有cookie内容:

响应这边:

 

访问setcookie请求的时候,代码中就会构造Cookie放在响应中.

进一步会写入浏览器,浏览器就会持久化保存.

 

③后续再次发送请求的时候,cookie内容就会出现在请求中:

再次发送,服务器就能拿到cookie内容了.(浏览器已经有了)

这个过程中,可以看到实际上真正发挥作用,还得是在服务器这边的逻辑中生效的. 

 

3. 理解会话机制

3.1 Session相关方法 

Servlet也提供了Session相关的支持.

实现登录功能,不需要直接使用Cookie AIP,直接使用Session的API就可以了.


①HttpServletRequest 类中的相关方法:

方法描述
HttpSession getSession()在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果 为 false, 则当不存在会话时返回 null

getSession方法中,最核心的API.

效果有两方面:

1.如果当前用户没有session(会话),就会创建出session.

2.如果已经有了session,就能查询到这个session.

getSession背后具体做的事情:

①先读取请求中的Cookie,看Cookie里是否有JSESSIONID属性,以及值是什么.

(平时说的SessionId是一个广义的概念,不同的库中具体实现过程中会有一些细节的差异,在Servlet中,把这个属性具体叫做JSESSIONID)

如果没有,就认为需要创建新对话,

如果有,就拿着这个Id去查询看看当前的Session是否存在.

  如果存在,就直接返回该Session

  如果不存在,就准备创建新对话.

②当前确实需要创建对话,就会创建出一个Session对象,同时生成唯一的一个JSESSIONID,以该JSESSIONID为key,Session对象为value,把这个键值对给插入到服务器上述的哈希表中.

③刚才生成的JSESSION又会通过addCookie方法,加入到响应中.

此时响应里就会带有Set-Cookie字段,这里的值就是JSESSION=xxxx.

通过响应,就把JSESSION返回到浏览器这边了.

问:

什么情况下会有SessionId但没有Session?

服务器这边存储上述这些Session对象也是在内存中进行的.

比如把SessionId返回给服务器了,SessionId和Session对象是保存在服务器内存的,此时如果服务器重启,内存中的这些会话数据就没了.

但是浏览器中的SessionId这个Cookie还是存在的.

Session保存在内存中,这样的设定其实也不是很合理.

因此实践中往往会把会话保存在其它的介质上,来达到"持久化存储"目的.


②HttpSession 类中的相关方法:

一个 HttpSession 对象里面包含多个键值对.

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

Session存在的意义,也是为了让用户能够保存一些自定义的数据.

此处Session更像是一个Map<String.Object>.

此时,程序员想保持什么样的键值对,就可以直接进行设置了.


3.2 Session整体的存储的数据结构

Session在一个服务器上,存在很多份,每个用户都应该有一个自己的Session.

服务器同时会有多个用户,服务器就会使用Map的方式来组织多个Session.

 

多个客户端对应多个键值对,key是sessionId,value是Session对象.

这些会话里面,每个会话又可以存一些用户的键值对(自定义的).

4. 实现用户登陆

4.1 前端

当前,页面是一个form表单.

当用户点击登陆按钮,就会发起一个HTTP请求. 

形如:

服务器这边就可以根据这个请求做出处理.

 

4.2 后端

4.2.1 登陆页面 

@WebServlet("/login")
public class LoginServlet extends HelloServlet{
    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取到用户的用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null || password == null || username.equals("") || password.equals("")) {
            //确定引用本身非空,内容非空
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("请求的参数不完整!");
            return;
        }

        //2.验证用户名密码是否正确.
        //正确的验证,是从数据库读取数据.
        //注册账号,就会给数据库插入用户名和密码.
        //登陆的时候,就是验证当前用户名是否存在,密码是否匹配.
        //当前为了简单,先不引入数据库,直接通过硬编码的方式来判定用户密码
        //此处约定,合法的用户名是zhangshan,密码123
        if (!username.equals("zhangshan")){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("用户名错误!");
            return;
        }
        if (!password.equals("123")){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("密码错误!");
            return;
        }

        //3.登陆成功!!
        //此时就可以给用户创建会话
        HttpSession session = req.getSession(true);
        //在会话中,可以顺便保存点自定义的数据,比如保存一个登陆的时间戳.
        //setAttribute后面的value是个Object,想存什么都可以.
        session.setAttribute("username",username);
        session.setAttribute("time",System.currentTimeMillis());
        
        //4.让页面自动跳转到网站主页.
        //此处约定主页的路径是index(也使用Servlet生成一个动态页面)
        resp.sendRedirect("index");
    }
}

 4.2.2 跳转页面

//通过这个Servlet生成一个主页
@WebServlet("/index")
public class IndexServlet extends HelloServlet{
    @Override
    //重定向的请求是Get请求
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先验证一下用户的登陆状态
        //如果未登陆,就要求用户先登陆
        HttpSession session = req.getSession(false);
        if (session == null){
            //用户未登陆
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("请先登陆!!!");
            return;
        }
        //已经登陆成功.
        //取出之前的attribute
        String username = (String) session.getAttribute("username");
        Long time = (Long) session.getAttribute("time");
        System.out.println("username="+username + ", time= "+time);

        //根据这样的内容去构造页面.
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("欢迎您, "+ username + "! 上次登陆时间: "+time);
    }
}

 

4.2.3 总结 

此时先访问index页面,是未登陆的状态:

此时故意输出用户名:

输入对的:

这个登陆时间,取决于我们的登陆时间,而不是访问时间.


总结:

这个程序就涉及到三个部分进行联动.

1.登陆页面(静态的html,使用form表单构造HTTP请求).

2.LoginServlet(doPost处理登陆的逻辑流程).

3.IndexServlet(doGet处理主页的生成).

在会话中存储的这些attribute,就相当于"埋下伏笔",后续其它页面中就可以使用到这里的东西.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux网络编程一(协议、TCP协议、UDP、socket编程、TCP服务器端及客户端)

文章目录 协议1、分层模型结构2、网络应用程序设计模式3、ARP协议4、IP协议5、UDP协议6、TCP协议 Socket编程1、网络套接字(socket)2、网络字节序3、IP地址转换4、一系列函数5、TCP通信流程分析 第二次更新&#xff0c;自己再重新梳理一遍… 协议 协议&#xff1a;指一组规则&…

16.Python多线程

如果想让我们的程序同时执行多个任务&#xff0c;就需要使用多线程技术了 。到目前为止&#xff0c;我们编写的程序都是单线程的&#xff0c;在运行时一次只能执行 一个任务。 1 线程相关的知识 1.1 进程 一个进程就是一个正在执行的程序&#xff0c;每一个进程都有自己独立…

C++ 基础复习

1.常量 #include<iostream> using namespace std; // 1.define 宏常量 #define N 50 int main(){//N 60; // define定义的数据为常量&#xff0c;一旦修改会报错 cout<<N<<endl;//2.const 修饰的常量 const int m 12; //m 24; //错误 const修饰的常量不能…

限速虚量就赔一万元?看我如何薅羊毛!2024公认最好的随身WiFi

作为一个大名鼎鼎的羊毛党&#xff0c;最热衷的莫过于网上各种可以薅羊毛的信息。这天我们的羊毛群里说有一个叫格行的随身WiFi品牌挺嚣张的&#xff0c;号称限速虚量就赔付一万元&#xff01;还发了带章的承诺函&#xff01;我心说随身WiFi限速虚量的臭名声早就烂大街了&#…

基本电路理论-电流和电压的参考方向

&#x1f308;个人主页&#xff1a;会编程的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” 电流及参考方向 电流&#xff1a;带电粒子有规则的定向移动 电流强度&#xff1a;单位时间内通过导体横截面的电荷量&#xff0c;即&#xff1a;idq/dt 单位&#xff1a…

【C++】lambda 表达式 / 包装器 / bind 绑定

目录 一. lambda 表达式1. lambda 表达式的语法1. lambda 表达式的使用2. lambda 表达式的捕捉列表 二. 包装器三. bind 绑定 一. lambda 表达式 Lambda 表达式是 C11 标准引入的一种新特性, 它提供了一种方便的方式来定义匿名函数. lambda 表达式实际上是一个匿名的仿函数; …

ZYNQ学习之Ubuntu下Linux文件系统、用户权限与磁盘管理

基本都是摘抄正点原子的文章&#xff1a;<领航者 ZYNQ 之嵌入式Linux 开发指南 V3.2.pdf&#xff0c;因初次学习&#xff0c;仅作学习摘录之用&#xff0c;有不懂之处后续会继续更新~ 一、Linux 文件系统 1.1 Linux 文件系统简介以及类型 操作系统的基本功能之一就是文件管…

SpringBoot快速入门笔记(1)

文章目录 一、环境准备1、maven2、新建项目版本问题 二、项目上手1、HelloController2、热部署3、控制器4、参数传递5、ParamsController 一、环境准备 1、maven 把下载的maven包给配置好 2、新建项目版本问题 新建项目发现没有Java8&#xff0c;新版本IDEA问题&#xff08;2…

JAVA基础02-Java语言基础以及编译准备工作

什么是JAVA语言 Java是一门面向对象的编程语言&#xff0c;不仅吸收了C语言的各种优点&#xff0c;还摒弃了C里难以理解的多继承、指针等概念&#xff0c;因此Java语言具有功能强大和简单易用的两个特征。 &#xff08;可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式…

HTTPS跟HTTP有区别吗?

HTTPS和HTTP的区别&#xff0c;白话一点说就是&#xff1a; 1. 安全程度&#xff1a; - HTTP&#xff1a;就像是你和朋友面对面聊天&#xff0c;说的话大家都能听见&#xff08;信息明文传输&#xff0c;容易被偷听&#xff09;。 - HTTPS&#xff1a;就像是你们俩戴着加密耳机…

idea 报错 Could not list the contents of folder “ftps

idea 报错 Could not list the contents of folder "ftps 解决方案 这里看到了网上的解决方案&#xff0c;顺便再记录一下。打开 【高级】菜单 - 取消勾选 被动模式。然后点击测试连接&#xff0c;显示连接成功&#xff01; ftp中的主动模式和被动模式 主动模式&…

嵌入式MCU和SOC之间的区别是什么?

今日话题&#xff0c;嵌入式MCU和SOC之间的区别是什么&#xff1f;表面上看&#xff0c;MCU代表嵌入式微控制器&#xff0c;而SOC代表片上系统&#xff0c;似乎只是嵌入式系统的不同称谓。然而&#xff0c;在实际的研发和产品设计中&#xff0c;你会发现它们在软硬件层面存在显…

【Python项目】基于django的【医用耗材网上申领系统】

医院信息化是社会发展的一个重要标志&#xff0c;它涉及到医院的各个方面&#xff0c;包括人员和物资&#xff0c;因此受到社会各界的广泛关注。近年来&#xff0c;随着医疗耗材数量的不断增加&#xff0c;如何有效管理这些耗材已经成为管理人员、医生以及社会各方面共同面临的…

数据结构(初阶)第二节:顺序表

从本文正式进入对数据结构的讲解&#xff0c;开始前友友们要有C语言的基础&#xff0c;熟练掌握动态内存管理、结构体、指针等章节&#xff0c;方便后续的学习。 顺序表&#xff08;Sequence List&#xff09; 线性表的概念&#xff1a;线性表&#xff08;linear list&#xff…

C++初阶:模拟实现vector类

模拟实现vector类 实现代码:vector.h #pragma once #include<assert.h> #include<iostream> using namespace std;namespace bit {template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_ite…

网络升级固件

资源信息 可知 &#xff1a; install\soc_cv1800b_milkv_duo_sd\boot.sd文件较设备中的同名文件多了128个字节的文件头&#xff1b;install\soc_cv1800b_milkv_duo_sd\rawimages\boot.sd文件与设备中同名文件相同&#xff1b; 环境搭建 服务器 启动TFTP服务 安装TFTP服务器…

vue2项目安装(使用vue-cli脚手架)

使用npm安装 安装镜像&#xff08;使npm创建项目更快&#xff09;&#xff1a;镜像可更换 npm config set registry https://registry.npmmirror.com1.全局安装vue-cli&#xff08;一次&#xff09; npm install -g vue/cli 2. 查看vue-cli 版本 vue --version 3. 创建项目…

Redis的安装部署

目录 1、关闭防火墙 2、yum安装gcc依赖包 3、提前准备好安装包并解压在opt目录下 4、进入redis目录下进行make编译 5、选择用于安装的目录 6、执行软件包提供的 install_server.sh 脚本文件设置 Redis 服务所需要的相关配置文件 7、一直回车直到出现以下提示 8、 把redi…

Java进阶-反射的详解与应用

本文深入探讨了Java反射机制的核心概念、应用实例及其在现代Java开发中的重要性。文章首先介绍了反射的基本原理和能力&#xff0c;包括在运行时动态获取类信息、操作对象字段和方法的能力。随后&#xff0c;通过具体代码示例&#xff0c;展示了如何利用反射进行字段访问、方法…