Linux与HTTP中的Cookie和Session

HTTP中的Cookie和Session

本篇介绍

前面几篇已经基本介绍了HTTP协议的大部分内容,但是前面提到了一点「HTTP是无连接、无状态的协议」,那么到底有什么无连接以及什么是无状态。基于这两个问题,随后解释什么是Cookie和Session,以及二者是如何通过代码实现的

本篇的代码部分都是基于上一节的HttpServer

什么是无连接和无状态

HTTP最大的特点就是无连接和无状态

所谓的无连接指的是HTTP请求服务器时不需要HTTP协议自己与服务器建立连接,由于HTTP是基于TCP的,所以整个连接工作交给了TCP,这一点在前面实现HttpServer时也有所体现

所谓的无状态指的是HTTP本身不会保存任何用户信息,举个例子,如果当前网站需要用户登录以进行后续操作,那么默认情况下,一旦这个用户登录成功后切换到其他页面就依旧会出现需要登录的情况

HTTP中的Cookie

基本介绍

既然HTTP是无状态的,那么正常情况下就会出现下面的情况:

在这里插入图片描述

上面图中这种情况的发送就会给用户带来糟糕的交互体验,也会给服务器带来比较大的开销,所以为了解决这个问题,就需要使用Cookie,但是因为HTTP本身是无连接的,所以Cookie本身并不是一种让HTTP从无连接变成有连接的技术,而是利用HTTP请求会携带报头的特点从而让HTTP每次请求时自动携带着Cookie

基于上面的情景,Cookie实际上实现的功能常见的就是保持用户的状态、记录用户偏好等

工作原理

Cookie工作方式与正常写入和读取HTTP请求报头属性比较类似:

  1. 当用户第一次访问网站时,服务器会在响应的HTTP头中设置Set-Cookie字段,用于发送Cookie到用户的浏览器
  2. 浏览器在接收到Cookie后,会将其保存在本地(通常是按照域名进行存储)在之后的请求中,浏览器会自动在HTTP请求头中携带Cookie字段,将之前保存的Cookie信息发送给服务器
  3. 在之后的请求中,浏览器会自动在HTTP请求头中携带Cookie字段,将之前保存的Cookie信息发送给服务器

Cookie的分类

Cookie一般情况下分为两种:

  1. 会话Cookie(Session Cookie):在浏览器关闭时失效
  2. 持久Cookie(Persistent Cookie):带有明确的过期日期或持续时间,可以跨多个浏览器会话存在

如果Cookie是一个持久性的,那么它其实就是浏览器特定目录下的一个文件。但直接查看这些文件可能会看到乱码或无法读取的内容,因为Cookie文件通常以二进制或sqlite格式存储。所以一般想查看Cookie都只能通过浏览器提供的入口进行查看

HttpServer中实现Cookie功能

前置知识

要想在HttpServer中实现给客户端添加Cookie的功能就需要用到一个响应报头属性:Set-Cookie,其值基本形式如下:

Set-Cookie: Cookie-name=Cookie-value

其中,Cookie-name代表的就是需要写入的Cookie的名称,Cookie-value对应的就是Cookie的值

除了上面的基本形式外,Cookie还有一种完整形式:

Set-Cookie: Cookie-name=Cookie-value; expires=Week, Day Month Year Hour:Minutes:Seconds UTC/GMT; path=Request-path; domain=domain-name; secure; Httponly

在上面的完整形式中,每一个字段的含义如下:

字段含义
Cookie-name=Cookie-valueCookie的名称和对应的值,是Set-Cookie中唯一必需的部分
expires指定Cookie的过期时间,格式为标准HTTP日期格式。如果不设置,则为会话Cookie(浏览器关闭即失效),例如expires=Wed, 21 Oct 2025 07:28:00 GMT
path指定Cookie生效的路径范围。例如:path=/admin/表示Cookie只在访问/admin/及其子路径时有效,默认为当前文档路径
domain指定Cookie生效的域名范围。例如:domain=example.com允许Cookie在该域名及其子域名下有效,默认为当前域名(不含子域名)
secure标记Cookie只能通过HTTPS安全连接传输,无值,存在即生效
HttpOnly禁止JavaScript通过document.cookie访问此Cookie,防止XSS攻击窃取Cookie,无值,存在即生效

需要注意的是,如果需要给客户端发送多个Cookie,则不可以直接在同一个Cookie后方拼接,而是需要另起一个Set-Cookie属性

因为Cookie有简单形式和完整形式两种,所以下面先演示简单形式,再扩展到完整形式,但是完整形式中不会演示所有属性,而是针对expirespath这两个属性进行解释

简单形式
写入Cookie

根据Cookie的工作原理第一条,首先需要在客户端第一次请求服务端时向客户端写入一条Cookie值,所以需要在响应报头中添加一条Set-Cookie属性,因为在上一节已经实现了一个登录函数:

void login(HttpRequest &req, HttpResponse &resp)
{LOG(LogLevel::INFO) << "进入登录模块";req.getParamKv();
}

所以本次考虑将添加Set-Cookie属性放到当前函数中,方式很简单,只需要调用HttpResponse类中的insertRespHead函数即可:

!!! note

本次为了方便就不处理是否是第一次添加或者之前添加的Cookie是否存在
void login(HttpRequest &req, HttpResponse &resp)
{   // ...// 添加Cookieresp.insertRespHead("Set-Cookie", "testCookie=newCookie");
}

添加完Cookie之后就需要向响应报头中写入这一条属性,所谓的写入就是进行序列化,所以接下来需要调用HttpResponse类中的serialize方法以及发送对应的响应报头给客户端,但是这一步实际上已经在处理HTTP请求函数handleHttpRequest做过了,所以就不需要再重复做了

完成上面的步骤后,接下来进行测试,需要注意,要让服务器执行login函数就必须请求/login方法,可以考虑直接在浏览器地址栏中请求/login并携带部分参数,此时使用的就是GET请求方式,也可以考虑走完整的步骤,先请求主页,再请求登录页面,通过登录请求/login资源。本次选择前者,但是为了能够看到具体的效果,需要考虑给响应体写入一点内容,确保浏览器可以停止在该页面,所以还需要调用一个设置响应行和响应体的方法,这里在HttpResponse中提供一个setRespLinesetRespBody方法:

=== “设置响应行”

// 设置响应行
void setRespLine(std::string& line)
{_resp_line = line;
}

=== “设置响应体”

void setRespBody(std::string& body)
{_resp_body = body;
}

接着,再在login函数中通过resp调用该方法设置响应行和响应体:

void login(HttpRequest &req, HttpResponse &resp)
{// ...// 设置响应行std::string line = default_http_ver + " " + std::to_string(200) + resp.setStatusCodeDesc(200);resp.setRespLine(line);// 设置响应体std::string body = "<h1>Hello Linux</h1>";resp.setRespBody(body);
}

测试结果如下:

在这里插入图片描述

但是,上面的方法只能实现发送一个Cookie给客户端,因为对于哈希表来说,key相同,新值会覆盖旧值,当前插入的key都是Set-Cookie,所以会出现新的Cookie键值对覆盖旧的Cookie键值对,为了解决这个问题,可以考虑使用一个vector单独保存每一条设置Cookie的字符串

所以,首先需要一个vector成员用于存储设置Cookie的字符串,接着在设置Cookie时,应该以一个完整的字符串存储到vector中,所以实际上需要下面的方法:

void insertRespCookies(const std::string &cookie_str)
{_cookies.push_back(cookie_str);
}

接着,在序列化时直接对每一个设置Cookie的字符串添加换行符:

// 序列化
bool serialize(std::string &out_str)
{// ...// 单独为Cookie添加\r\nstd::for_each(_cookies.begin(), _cookies.end(), [&](std::string s){s += default_sep;out_str += s;});// ...return true;
}

最后,修改设置Cookie的login函数:

void login(HttpRequest &req, HttpResponse &resp)
{// ...// 添加Cookie// resp.insertRespHead("Set-Cookie", "testCookie=newCookie");resp.insertRespCookies("Set-Cookie: testCookie=newCookie");resp.insertRespCookies("Set-Cookie: testCookie1=newCookie1");resp.insertRespCookies("Set-Cookie: testCookie2=newCookie2");// ...
}

编译运行上面的代码即可看到多条Cookie:

在这里插入图片描述

读取Cookie

当前,服务端已经可以向客户端写入Cookie信息,但是光写入还并不够,服务端还需要读取到对应的Cookie并对Cookie进行解析,因为客户端发送的Cookie形式为:

Cookie: testCookie=newCookie

所以需要根据这个字段格式进行解析

在前面实现HttpRequest中已经实现了读取请求报头中的数据函数getPairFromReqHead,所以在该类中存在一个成员keyCookievaluetestCookie=newCookie的键值对

首先根据key找到对应的value,再将value根据拆分出每一个Cookie

需要注意,尽管服务端是分开设置多个Cookie,但是在客户端给服务端发送Cookie时是合并在一个Cookie中,每一个Cookie以;<space>进行分隔,例如:

```
Cookie: testCookie=newCookie; testCookie1=newCookie1
```

接下来的问题就是如何拆分Cookie,拆分Cookie的思路和拆分请求体参数的方式非常类似,而因为要存储每一个Cookie键值对,所以还需要一个哈希表,所以基本结构如下:

// 键值对分隔符
const std::string default_kv_sep = "=";
// Cookie分隔符
const std::string default_cookie_sep = "; ";// HTTP请求
class HttpRequest
{
public:// ...// 获取Cookie键值对bool getCookiePairFromReqHead(){}// ...private:// ...std::unordered_map<std::string, std::string> _cookie_kv; // Cookie键值对// ...
};

接着,根据前面实现从请求体中获取参数键值对的思路实现获取Cookie键值对:

// 获取Cookie键值对
bool getCookiePairFromReqHead()
{// 先提取到Cookieauto keyPos = _kv.find("Cookie");if (keyPos == _kv.end())return false;// 在对应的value中找到Cookie值std::string cookie_value = _kv["Cookie"];auto pos = size_t(-1);while (true){// 找到;auto pos1 = cookie_value.find(default_cookie_sep, pos + 1);if (pos1 == std::string::npos){// 最后一个参数键值对auto pos_t = cookie_value.find(default_kv_sep, pos + 1);if (pos_t == std::string::npos)return false;std::string key = cookie_value.substr(pos + 1, pos_t - pos - 1);std::string value = cookie_value.substr(pos_t + 1);_cookie_kv.insert({key, value});break;}// 找到=auto pos2 = cookie_value.find(default_kv_sep, pos + 1);if (pos2 == std::string::npos)return false;std::string key = cookie_value.substr(pos + 1, pos2 - pos - 1);std::string value = cookie_value.substr(pos2 + 1, pos1 - pos2 - 1);_cookie_kv.insert({key, value});// 修改起始位置pos = pos1 + 1;}return true;
}

在上面的代码中,除了函数开始的逻辑与前面获取POST请求参数的逻辑不一样以外,还有更新pos的逻辑,因为不同的Cookie之间的分隔符是两个字符,而不是一个字符

接下来,为了可以看到拆分后的结果,提供一个打印函数:

void getCookieKv()
{std::for_each(_cookie_kv.begin(), _cookie_kv.end(), [&](std::pair<std::string, std::string> kv){ std::cout << kv.first << ":" << kv.second << std::endl; });
}

有了上面的函数后,接下来就是在指定位置调用该函数:

// 反序列化
bool deserialize(std::string &in_str)
{// ...// 获取CookiegetCookiePairFromReqHead();// 打印获取到的CookiegetCookieKv();// ...return true;
}

编译运行上面的代码,观察结果:

在这里插入图片描述

从上面的结果可以看到,所有设置到客户端的Cookie都可以正常获取

完整形式
介绍

上面已经基本实现了简单形式下Cookie的写入和读取,但是有时简单的Cookie并不能满足实际的需求,所以除了完成简单形式以外,还需要实现完整形式的Cookie

虽然是完整形式,但是客户端最后拿到的Cookie会被浏览器进行分析,并将相关的属性填充到浏览器的相关属性中,而客户端第二次以及后面请求服务器时,携带的Cookie也只是上面简单形式的Cookie

基于上面的原因,在完整形式部分只需要实现形成某些字段的函数即可

本次只是实现完整形式中的expirespath,其他字段不考虑

实现expires

在实现expires字段之前,先了解其格式:

expires=Week, Day Month Year Hour:Minutes:Seconds UTC/GMT;

第一个值为星期,第二个值为年月日,第三个值为时分秒,最后一个代表时区,UTC代表协调世界时,GMT代表格林威治平均时间,对于前面的三个字段来说都可以通过系统调用进行获取,唯独最后一个字段需要手动指定,那么选择UTC还是GMT?推荐UTC,其次再是GMT,原因如下:

UTC全称为Coordinated Universal Time (协调世界时),是现代国际社会使用的主要时间标准:基于高精度的原子钟,不受地球自转不规则性的影响,是国际标准化组织ISO推荐的官方时间标准。是现在全球互联网通信和大多数计算机系统的时间基准,格式精确统一,适合跨系统通信

GMT全称为Greenwich Mean Time (格林威治平均时间):历史上的时间标准,基于英国伦敦格林威治天文台的太阳时,其标准基于地球自转,存在微小的不规则性,在科学和技术领域已逐渐被UTC替代,但在日常交流中仍被广泛使用

在Cookie的expires字段中:推荐使用UTC:更精确,是现代网络协议的标准做法。GMT仍然被广泛支持,大多数浏览器能正确处理。实际上两者时间差异极小(不到1秒),但从标准化和兼容性考虑,优先选择UTC

有了知识基础,接下来就是实现获取时间函数,在前面命名管道与共享内存和日志系统已经实现或者使用了一个获取时间的函数,本次实现的思路和该函数基本一致,但是当时使用的是localtime或者localtime_r函数,这两个函数都会自动包含时区,而实际上在设置expires时不需要设置时区,当客户端获取到时间后会自动根据当前浏览器所处的地区设置时区,所以这里推荐使用另外一个系统调用gmtime或者其可重入版本gmtime_r。下面以gmtime为例,函数原型如下:

struct tm *gmtime(const time_t *timep);

使用方式与localtime一样,此处不再介绍。根据格式Wed, 21 Oct 2025 07:28:00 GMT,首先需要两个额外的函数,根据月份数字获取月份字符串和根据星期数字获取星期字符串,接着再使用这两个函数获取一个完整的expires时间:

=== “根据星期数字获取星期字符串”

std::string getWeekStrFromNumber(int w)
{// 第一个元素最好是星期天,因为gmtime以星期天为第一天const char *weekdays[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};if (w < 0 || w > 12)return std::string();return weekdays[w];
}

=== “根据月份数字获取月份字符串”

std::string getMonthStrFromNumber(int m)
{const char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};if (m < 0 || m > 12)return std::string();return months[m];
}

=== “获取expires函数”

 std::string getCookieExpiresTime(int last){// 获取指定的时间之后的时间戳time_t t = time(NULL) + last;struct tm *timer = gmtime(&t);char buffer[1024] = {0};// Wed, 21 Oct 2025 07:28:00 GMT// 注意,涉及到数字的都需要两位,确保一位数可以有前导0snprintf(buffer, sizeof(buffer), "%s, %02d %s %d %02d:%02d:%02d UTC", getWeekStrFromNumber(timer->tm_wday).c_str(),timer->tm_mday,getMonthStrFromNumber(timer->tm_year).c_str(),timer->tm_year + 1900,timer->tm_hour,timer->tm_min,timer->tm_sec);return buffer;}

接着,在login函数中创建一个带有expires的Cookie,过期时间设置为1分钟:

void login(HttpRequest &req, HttpResponse &resp)
{// ...std::string cookie_val = "testCookie=newCookie; expires=" + resp.getCookieExpiresTime(60);resp.insertRespCookies("Set-Cookie: " + cookie_val);// ...
}

编译运行上面的代码,观察结果:

在这里插入图片描述

在控制台打印出实际获取到的时间如下:

在这里插入图片描述

可以看到,这个时间虽然没有时区,但是在浏览器显示时已经会转换成当地时区

实现path

path的实现很简单,只需要在login中设置Cookie时传递path即可:

void login(HttpRequest &req, HttpResponse &resp)
{// ...resp.insertRespCookies("Set-Cookie: testCookie=newCookie; path=/login.html");// ...
}

先设置Cookie:

在这里插入图片描述

此时,testCookie=newCookie一旦被设置,下一层就只会在客户端访问login.html时才会携带该Cookie:

=== “访问主页”

在这里插入图片描述

=== “访问login.html

在这里插入图片描述

Cookie的安全性

在上面的测试中可以发现,不论Cookie是从服务端到客户端,还是客户端到服务端,都是可以在客户端以明文的方式看到的,如果用户输入的是账户名和密码或者其他隐私信息,那么一旦这个Cookie被劫持,就会出现用户信息泄露的问题,这对用户来说是较大的损失,所以Cookie本身是不安全的

HTTP中的Session

基本介绍

上面提到Cookie是不安全的,为了尽可能保证安全这个问题,在现代HTTP中除了使用Cookie外还会使用Session

Session与Cookie不同,Session是存储在服务器端的,当用户第一次请求服务端时,服务端会将用户的信息存储到一个特定的位置,再向客户端写入一个值为sessionID的Cookie,当用户第二次请求服务器时就会携带这个值sessionID的Cookie访问服务器,服务器会根据这个SessionID查找用户的信息,从而实现用户状态的保持

HttpServer中实现Session

要在HttpServer中实现Session,首先可以考虑创建一个Session类,这个类用于存储一个用户的相关信息,相当于之前包含用户信息的Cookie:

class Session
{
public:Session(const std::string &username, const std::string &status):_username(username), _status(status){_create_time = time(nullptr); // 获取时间戳就行了,后面实际需要,就转化就转换一下}~Session(){}
public:std::string _username;std::string _status;uint64_t _create_time;
};

因为服务器获取到的肯定不止一个Session,所以还需要一个类对Session类对象进行管理,此处可以考虑创建一个SessionManager类,因为sessionID是一个随机数,并且一般不能出冲突情况,所以这里考虑使用boost库中获取UUID的函数generator

using session_ptr = std::shared_ptr<Session>;class SessionManager
{
public:SessionManager(){}std::string addSession(session_ptr s){// 使用boost库生成uuid// 创建一个随机数生成器boost::uuids::random_generator generator;// 生成一个随机 UUIDboost::uuids::uuid id = generator();std::string sessionid = boost::uuids::to_string(id);_sessions.insert(std::make_pair(sessionid, s));return sessionid;}session_ptr getSession(const std::string sessionid){if (_sessions.find(sessionid) == _sessions.end())return nullptr;return _sessions[sessionid];}~SessionManager(){}private:std::unordered_map<std::string, session_ptr> _sessions;
};

首先考虑在HttpRequest中创建一个SessionManager类对象指针,并使用SessionManager类对象进行初始化。接着在反序列化时查找出Cookie中是否有sessionid,如果有就根据这个sessionid查找是否已经存在于SessionManager中,如果不存在就创建一个Session对象并插入到SessionManager

但是,在上面创建Session对象时需要使用用户名创建,而这个用户名来自请求参数,如果没有请求参数就不需要创建Session对象,所以需要对前面获取参数的函数进行修改,使其有返回值:

bool getReqParams()
{if (_req_method == "GET"){// 处理GET请求中的参数if (getReqParamsFromReqLine()){_hasArgs = true;return true;}}else if (_req_method == "POST"){// 处理POST请求中的参数if (getReqParamsFromBody()){_hasArgs = true;return true;}}return false;
}

接着,在HttpRequest类中添加一个成员_sessionid,并在deserialize函数中补充逻辑:

bool deserialize(std::string &in_str)
{// ...// 获取参数内容if(getReqParams()) {// 判断制定的sessionid是否存在,不存在则创建if (!_sm->getSession(getSessionIdFromCookiePair())){std::string username = _param_kv["username"];// 根据参数的username创建Session对象,并处于活跃状态std::shared_ptr<Session> s = std::make_shared<Session>(username, "active");// 插入到SessionManager中_sessionid = _sm->addSession(s);}}return true;
}

接着,在用户请求/login时,创建一个Cookie,这个Cookie的值即为sessionid,所以此处还需要在HttpRequest中提供一个获取_sessiondid的函数:

// 获取_sessionid
std::string getSessionId()
{return _sessionid;
}

修改login函数如下:

void login(HttpRequest &req, HttpResponse &resp)
{// ...std::string sessionid = req.getSessionId();std::string cookie_val = "sessionid=" + sessionid;resp.insertRespCookies("Set-Cookie: " + cookie_val);// ...
}

编译上面的代码,观察结果:

在这里插入图片描述

Session的安全性

Session相比Cookie具有明显的安全优势。由于Session将用户敏感数据存储在服务器端,而不是客户端,只通过SessionID标识用户,大大降低了信息泄露风险。客户端仅存储一个不包含实际用户数据的SessionID,即使这个ID被截获,攻击者也无法直接获取用户的实际信息,如密码和个人资料

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

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

相关文章

Redis哨兵模式(Sentinel)高可用方案介绍与配置实践

Redis Sentinel 是 Redis 官方提供的高可用性&#xff08;HA&#xff09;解决方案&#xff0c;用于管理 Redis 主从架构中的故障检测和故障转移。通过 Redis Sentinel&#xff0c;可以实现 Redis 主从集群的自动故障恢复&#xff0c;确保服务的高可用性。本文将详细介绍Redis S…

【深度】JADC2的层级结构以及全域Mesh网络

文章目录 内容摘要1. 引言2. JADC2层级结构3. JADC2转变为CJADC24. 与工业领域自动化金字塔和全域MESH网络的异同4.1 工业领域自动化金字塔4.2 全域Mesh网络 #JADC2 #Mesh网络 #融合计划 #ABMS #超越计划 #人工智能 #普罗米修斯 **专栏说明&#xff1a;主要研究作战概念、新型作…

210、【图论】课程表(Python)

题目 思路 这道题本质上是一个拓扑排序。每次先统计每个点的入度个数、然后再统计点与点之间的邻接关系&#xff0c;找到入度为0的点作为起始遍历点。之后每遍历到这个点之后&#xff0c;就把这个点后续的邻接关系边的点入度减去一。当某个点入度为0时&#xff0c;继续被加入其…

Mock接口编写教程-axios-mock-adapter(React)

Mock模拟接口编写教程 直接在前端实现接口模拟 1.第一步 设置模拟接口 // mock.ts import axios from axios import MockAdapter from axios-mock-adapter// 创建一个模拟适配器 const mock new MockAdapter(axios)// 设置模拟接口 export const setupMock () > {mock.…

CCF 编程能力认证 C++ 四级宝典

CCF编程能力等级认证&#xff08;以下简称GESP&#xff09;2025年四次认证时间分别为&#xff1a;3月22日、6月28日、9月27日、12月20日&#xff0c;认证方式为线下机考&#xff0c;认证语言包括&#xff1a;C、Python和Scratch三种语言&#xff0c;其中Scratch认证为一到四级&…

OpenCV图像拼接(4)图像拼接模块的一个匹配器类cv::detail::BestOf2NearestRangeMatcher

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::detail::BestOf2NearestRangeMatcher 是 OpenCV 库中用于图像拼接模块的一个匹配器类&#xff0c;专门用于寻找两幅图像之间的最佳特征点匹配…

【C#语言】C#中的同步与异步编程:原理、示例与最佳实践

文章目录 ⭐前言⭐一、同步编程&#xff1a;简单但低效的线性执行&#x1f31f;代码示例&#x1f31f;执行流程示意图&#x1f31f;同步编程特点 ⭐二、异步编程&#xff1a;非阻塞的高效执行&#x1f31f;代码示例&#x1f31f;执行流程示意图&#x1f31f;异步编程核心机制&a…

el-input 不可编辑,但是点击的时候出现弹窗/或其他操作面板,并且带可清除按钮

1.focus“getFocus”鼠标聚焦的时候写个方法&#xff0c;弹窗起来 getFocus(){ this.定义的弹窗状态字段 true;} 2.点击确定的时候&#xff0c;数值赋值到el-input的输入框,弹窗取消&#xff08;this.定义的弹段字端 false&#xff09; 3.但是会有个问题就是el-input 不可点…

事件响应计划:网络弹性的关键

网络安全事件响应计划不仅仅是技术上的需要&#xff0c;更是企业的当务之急。在网络威胁比以往任何时候都更加复杂和频繁的时代&#xff0c;了解并做好准备应对这些事件可能会决定是恢复还是灾难。 以下是简要分析&#xff1a; 网络安全事件不仅仅是技术故障&#xff1b;它们…

正则表达式详解(regular expression)

&#x1f4a1; 正则表达式&#xff08;Regular Expression, regex&#xff09;知识点总结 &#x1f4a1; 正则表达式是一种用于匹配字符串的模式&#xff0c;广泛用于搜索、替换、验证等操作。 &#x1f4cc; 正则表达式的主要作用 1️⃣ 字符串匹配 &#x1f9d0; 检查一个…

全球化2.0 | ZStack云计算系统工程师(ZCCE)国际认证培训成功举办

近日&#xff0c;ZStack云计算系统工程师&#xff08;ZCCE&#xff09;国际认证培训在上海成功举办。本次培训采用 "线下 线上直播" 双轨模式&#xff0c;同步开设中文与英文课程&#xff0c;吸引了来自中国、东南亚、独联体、北美、中东等多个国家和地区的近 90 名…

C++学习之nginx+fastDFS

目录 1.知识点概述 2.fastcgi复习 3.文件上传流程分析 4.文件下载流程和优化 5.在存储节点上安装nginx和fastdfs插件 6.mod-fdsf.com配置文件修改 7.解决nginx的worker无法启动&#xff0c;拷贝配置文件操作 8.客户 通过浏览器访问存储节点404问题 9.nginx服务器处理资…

【AIGC】Win10系统极速部署Docker+Ragflow+Dify

【AIGC】WIN10仅3步部署DockerRagflowDify 一、 Docker快速部署1.F2进入bios界面&#xff0c;按F7设置开启VMX虚拟化技术。保存并退出。2.打开控制面板配置开启服务3.到官网下载docker安装包&#xff0c;一键安装&#xff08;全部默认勾选&#xff09; 二、 RagFlow快速部署1.确…

token升级(考虑在分布式环境中布置token,结合session保证请求调用过程中token不会过期。)

思路&#xff1a; 首先&#xff0c;用户的需求是确保使用同一个Token的外部调用都在一个Session中处理。 需要考虑Token与Session绑定、安全措施、Session管理、分布式处理等。 使用Redis作为Session存储&#xff0c; 在Java中 通过Spring Data Redis或Lettuce库实现。 2.生成…

新一代电子数据取证专家 | 苏州龙信信息科技有限公司

本文关键词&#xff1a;电子取证、手机取证、计算机取证、云取证 关于我们About us 苏州龙信信息科技有限公司专注于电子数据取证、大数据、信息安全等领域&#xff0c;核心业务主要涵盖取证工具研发、大数据融合分析、案件技术支持、取证能力培训等&#xff0c;先后为执法部门…

研究生研究方向系统基于springboot SSM

目录 摘要 一、系统背景与目的 二、开发流程 三、系统架构与技术选型 四、功能分析 4.1 用户角色与权限管理 4.2研究方向管理功能 4.3学习资源管理功能 4.4科研项目跟踪功能 4.5学术交流与分享功能 4.6导师指导与评估功能 摘要 基于Spring Boot的研究生研究方向系统…

[解决] PDF转图片,中文乱码或显示方框的解决方案

在Java开发中,将PDF文件转换为图片是一项常见的需求,但过程中可能会遇到中文乱码或显示方框的问题。本文将深入探讨这一问题,并提供详细的解决方案,帮助开发者顺利地完成PDF到图片的转换。 一、问题现象 在使用Java库(如Apache PDFBox)将PDF转换为图片时,如果PDF文件中…

「JavaScript深入」WebSocket:高效的双向实时通信技术

WebSocket WebSocket 的特点1. 全双工通信2. 持久连接3. 低延迟4. 二进制和文本支持5. 连接管理6. 二进制数据传输 WebSocket 协议详解1. 握手过程2. 数据帧结构 WebSocket 的实现服务器端实现&#xff08;Node.js ws库&#xff09;1. 基础服务器2. 广播功能实现3. 心跳机制客…

ABAP 长文本编辑器

加个屏幕 *&---------------------------------------------------------------------* *& Report YDEMO2 *&---------------------------------------------------------------------* *& *&---------------------------------------------------------…

postman小白教程(从入门到实战,详细教学)

目录 1. postman介绍 2. 下载地址 3. 安装流程 4. 注册postman账号 ① 打开postman&#xff0c;点击【创建账号】或【登录】&#xff0c;会跳转到浏览器 ② 若已有账号可以直接登录&#xff1b;若无账号&#xff0c;则创建新账号 ③ 若登录成功会弹出提示框&#xff0c;…