解决前后端发送验证码手机号登陆的sessionId不一致问题

前端:鸿蒙开发ArkTs语言
后端:spring boot mybatis-plus框架

后端代码

 @PostMapping("/sendMsg")public R<String> sendMsg(@RequestBody User user, HttpServletRequest request, HttpServletResponse response) {// 从User对象中提取用户手机号String phone = user.getPhone();// 验证手机号是否不为空if (StringUtils.isNotEmpty(phone)) {// 生成4位随机验证码String code = ValidateCodeUtils.generateValidateCode(4).toString();// 获取或创建Session,并获取Session IDHttpSession session = request.getSession(true);String sessionId = session.getId();// 发送短信验证码,使用第三方服务或APISMSUtils.sendMessage("鸿蒙开发", "SMS_475965454", phone, code);// 将验证码存储在Session中,以便后续验证session.setAttribute("SMS_CODE_" + phone, code);// 设置Session的超时时间为300秒(5分钟)session.setMaxInactiveInterval(300);// 记录日志,包含手机号、验证码和Session IDlog.info("Stored code in session - Phone: {}, Code: {}, SessionId: {}",phone, code, sessionId);// 创建JSESSIONID Cookie,用于维持会话状态Cookie cookie = new Cookie("JSESSIONID", sessionId);// 设置Cookie的路径,使得整个应用都可以访问该Cookiecookie.setPath("/");// 设置Cookie的HttpOnly属性为true,增加安全性cookie.setHttpOnly(true);// 将Cookie添加到响应中response.addCookie(cookie);// 返回成功的响应,表示短信验证码已发送return R.success("手机验证码短信发送成功");}// 如果手机号为空,返回失败的响应return R.error("发送失败");}/*** 处理手机号验证码登录的请求。* 该方法处理HTTP POST请求到"/loginByPhone"路径,接收登录参数和HTTP会话,返回登录结果。** @param map 包含登录参数的Map对象,其中应包含手机号和验证码。* @param session HTTP会话对象,用于验证验证码和存储用户信息。* @return R<User> 封装了登录结果的响应对象,包含登录成功或失败的信息。*/@PostMapping("/loginByPhone")public R<User> loginByPhone(@RequestBody Map<String, String> map, HttpSession session) {// 从请求体中提取手机号和验证码String phone = map.get("phone");String code = map.get("code");// 记录尝试登录的日志信息log.info("Attempting phone login - Phone: {}, Input Code: {}", phone, code);// 1. 验证手机号和验证码非空// 检查手机号和验证码是否为空,如果为空返回错误信息if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {return R.error("手机号或验证码不能为空");}// 2. 验证码校验// 从会话中获取与手机号关联的验证码Object codeInSession = session.getAttribute("SMS_CODE_" + phone);// 记录验证码校验的日志信息log.info("Phone: {}, Input Code: {}, Stored Code: {}", phone, code, codeInSession);// 检查会话中是否存在验证码,如果不存在返回验证码过期的错误信息if (codeInSession == null) {log.warn("No verification code found in session for phone: {}", phone);return R.error("验证码已过期,请重新获取");}// 比较输入的验证码和会话中存储的验证码,如果不一致返回验证码错误信息if (!codeInSession.toString().equals(code)) {log.warn("Invalid verification code. Expected: {}, Got: {}", codeInSession, code);return R.error("验证码错误");}// 3. 查询用户是否存在// 创建查询包装器,设置查询条件为手机号等于请求中的手机号LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getPhone, phone);// 根据查询条件获取用户信息User user = userService.getOne(queryWrapper);// 4. 不存在则创建新用户// 如果用户不存在,创建新用户对象,并设置手机号和随机生成的用户名if (user == null) {user = new User();user.setPhone(phone);// 生成随机用户名(手机号后4位+随机4位数字)String randomUsername = "user_" + phone.substring(7) +String.format("%04d", new Random().nextInt(10000));user.setUsername(randomUsername);// 使用默认密码123456,并加密String defaultPassword = "123456";user.setPassword(CustomPasswordEncoder.encode(defaultPassword));  // 确保使用加密user.setStatus(1);  // 设置用户状态为启用// 保存用户信息userService.save(user);// 记录新用户创建的日志信息log.info("New user created - Phone: {}, Username: {}, Default password set", phone, randomUsername);// 设置credentials信息,包含用户名和默认密码Map<String, String> credentials = new HashMap<>();credentials.put("username", randomUsername);credentials.put("password", defaultPassword);  // 返回未加密的默认密码user.setCredentials(credentials);}// 5. 清除验证码// 从会话中移除验证码,确保验证码只能使用一次session.removeAttribute("SMS_CODE_" + phone);// 6. 将用户ID存入Session// 将用户ID存储在会话中,以便后续请求可以识别用户session.setAttribute("user", user.getId());// 记录用户登录成功的日志信息log.info("User logged in successfully - Phone: {}, UserId: {}", phone, user.getId());// 返回成功的响应,包含用户信息return R.success(user);}

前端model代码

/*** 使用手机号和验证码进行登录的方法。* 该方法向服务器发送POST请求,包含手机号和验证码,以验证用户身份。** @param phone 手机号* @param code 验证码* @return Promise<UserInfo> 一个Promise对象,包含登录结果,成功时解析为用户信息,失败时拒绝。*/loginByPhone(phone: string, code: string): Promise<UserInfo> {return new Promise((resolve, reject) => {// 创建HTTP请求实例let httpRequest = http.createHttp();// 从存储中获取保存的cookieconst sessionCookie = AppStorage.Get<string>('sessionCookie');// 调用getRequestOptions方法来设置请求选项,并附加登录数据const requestOptions = this.getRequestOptions(http.RequestMethod.POST, {phone: phone,code: code});// 如果存在cookie,则添加到请求头中if (sessionCookie) {requestOptions.header['Cookie'] = sessionCookie;}// 打印请求选项的日志信息console.log('Login request options:', requestOptions);// 发起HTTP请求到后端的/loginByPhone端点httpRequest.request(`${this.baseURL}/loginByPhone`,requestOptions).then(resp => {// 打印响应的日志信息console.log('Login response:', resp);// 如果响应代码是200,表示请求成功if (resp.responseCode === 200) {// 解析响应体中的JSON数据const responseData = JSON.parse(resp.result.toString());// 如果响应码是1,表示操作成功if (responseData.code === 1) {// 保存登录状态到本地存储AppStorage.SetOrCreate('isLoggedIn', true);AppStorage.SetOrCreate('userInfo', JSON.stringify(responseData.data));// 解决Promise并返回用户信息resolve(responseData.data);} else {// 如果响应码不是1,拒绝Promise并返回错误信息reject(responseData.msg || '登录失败');}} else {// 如果响应代码不是200,拒绝Promise并返回登录失败信息reject('登录失败');}}).catch(error => {// 如果请求过程中出现错误,打印错误日志并拒绝Promiseconsole.error('Login error:', error);reject(error);}).finally(() => {// 不管请求成功与否,最终都要销毁HTTP请求实例,释放资源httpRequest.destroy();});});}/*** 发送验证码的方法。* 该方法向服务器发送POST请求,请求发送验证码到指定的手机号。** @param phone 接收验证码的手机号* @return Promise<void> 一个Promise对象,表示发送验证码操作的异步结果。*/sendVerificationCode(phone: string): Promise<void> {return new Promise((resolve, reject) => {// 创建HTTP请求实例let httpRequest = http.createHttp();// 打印日志,显示正在向哪个手机号发送验证码console.log('Sending code to phone:', phone);// 向服务器的/sendMsg端点发送POST请求,请求发送验证码httpRequest.request(`${this.baseURL}/sendMsg`,// 使用之前定义的getRequestOptions方法来设置请求选项,并附加手机号信息this.getRequestOptions(http.RequestMethod.POST, { phone: phone })).then(resp => {// 打印日志,显示发送验证码的响应结果console.log('Send code response:', resp);// 从响应头中获取Set-Cookie字段,保存登录会话的cookieconst cookies = resp.header['set-cookie'];if (cookies) {console.log('Received cookies:', cookies);// 将cookie存储到应用存储中,以便后续请求使用AppStorage.SetOrCreate('sessionCookie', cookies[0]);}// 如果响应代码是200,表示请求成功if (resp.responseCode === 200) {// 解析响应体中的JSON数据const responseData = JSON.parse(resp.result.toString());// 如果响应码是1,表示操作成功if (responseData.code === 1) {// 解决Promiseresolve();} else {// 如果响应码不是1,拒绝Promise并返回错误信息reject(responseData.msg || '发送验证码失败');}} else {// 如果响应代码不是200,拒绝Promise并返回发送验证码失败信息reject('发送验证码失败');}}).catch(error => {// 如果请求过程中出现错误,打印错误日志并拒绝Promiseconsole.error('Send code error:', error);reject(error);}).finally(() => {// 不管请求成功与否,最终都要销毁HTTP请求实例,释放资源httpRequest.destroy();});});}

发送验证码流程详解
前端发送验证码请求 (客户端)

  1. 用户输入手机号
  2. 调用 sendVerificationCode(phone) 方法
  3. 构造 POST 请求到 /sendMsg 端点
  4. 携带手机号信息
    后端处理验证码发送 (服务端)
  5. 接收到前端请求后,@PostMapping(“/sendMsg”) 方法被触发
  6. 验证手机号是否非空
  7. 生成4位随机验证码
  8. 关键操作 - 会话管理:
    ○ 创建 HttpSession
    ○ 获取 sessionId
    ○ 将验证码存储在 Session 中,键为 “SMS_CODE_” + phone
    ○ 设置 Session 超时时间为 5 分钟
    会话安全机制
    ● 使用 HttpSession 存储验证码
    ● 生成唯一的 sessionId
    ● 创建 JSESSIONID Cookie
    ● Cookie 设置为 HttpOnly,防止跨站脚本攻击
    ● Cookie 路径设置为 “/”,整个应用可访问
    发送短信
    ● 调用 SMSUtils.sendMessage() 发送验证码
    ● 使用第三方短信服务
    ● 记录发送日志
    手机号登录流程详解
    前端登录请求
    1.用户输入手机号和收到的验证码
  9. 调用 loginByPhone(phone, code) 方法
  10. 构造 POST 请求到 /loginByPhone 端点
  11. 携带手机号和验证码
  12. 附带之前获得的 sessionCookie
    后端登录验证 (服务端)
  13. 接收到前端请求
  14. 验证码校验流程:
    ○ 检查手机号和验证码是否为空
    ○ 从 Session 中获取存储的验证码
    ○ 比较输入的验证码与存储的验证码
    ○ 验证通过后,立即删除 Session 中的验证码(一次性)
  15. 用户处理:
    ○ 根据手机号查询用户是否存在
    ○ 不存在则自动创建新用户
    ■ 生成随机用户名
    ■ 设置默认密码
    ■ 保存用户信息
  16. 登录成功后:
    ○ 将用户 ID 存入 Session
    ○ 返回用户信息

Session ID 一致性的关键机制
会话同步关键步骤

  1. 发送验证码时:
HttpSession session = request.getSession(true);
String sessionId = session.getId();

○ 创建/获取 Session
○ 生成唯一 sessionId
2. 设置 Cookie:

Cookie cookie = new Cookie("JSESSIONID", sessionId);
cookie.setPath("/");
cookie.setHttpOnly(true);
response.addCookie(cookie);

○ 将 sessionId 作为 Cookie 值
○ 设置 Cookie 路径为全局
○ 设置 HttpOnly 增加安全性
3. 前端保存 Cookie:

const cookies = resp.header['set-cookie'];
if (cookies) {AppStorage.SetOrCreate('sessionCookie', cookies[0]);
}

○ 保存服务端返回的 Cookie
○ 后续请求携带此 Cookie
4. 登录时附带 Cookie:

const sessionCookie = AppStorage.Get<string>('sessionCookie');
if (sessionCookie) {requestOptions.header['Cookie'] = sessionCookie;
}

○ 登录请求携带原始 Cookie
○ 确保 sessionId 一致

在这个手机验证登录流程中,Cookie(特别是JSESSIONID Cookie)扮演了非常重要的会话管理和状态维持的角色。

  1. Cookie的基本目的 在这个应用程序中,Cookie主要用于在客户端和服务器之间维持会话状态。具体来说,JSESSIONID Cookie负责在不同的HTTP请求之间保持用户的会话标识。
  2. 在服务端发送Cookie的过程 在sendMsg方法中,服务器明确创建并发送了一个Cookie:
// 创建JSESSIONID Cookie,用于维持会话状态
Cookie cookie = new Cookie("JSESSIONID", sessionId);
// 设置Cookie的路径,使得整个应用都可以访问该Cookie
cookie.setPath("/");
// 设置Cookie的HttpOnly属性为true,增加安全性
cookie.setHttpOnly(true);
// 将Cookie添加到响应中
response.addCookie(cookie);

这段代码做了几个关键的事情:
● 创建了一个名为"JSESSIONID"的Cookie
● 设置Cookie的值为当前会话的Session ID
● 将Cookie的路径设置为"/",意味着整个应用都可以访问这个Cookie
● 设置了HttpOnly标志,防止客户端脚本访问Cookie,提高安全性

  1. 客户端处理Cookie的方式 在客户端的sendVerificationCode方法中,可以看到对Cookie的处理:
// 从响应头中获取Set-Cookie字段,保存登录会话的cookie
const cookies = resp.header['set-cookie'];
if (cookies) {console.log('Received cookies:', cookies);// 将cookie存储到应用存储中,以便后续请求使用AppStorage.SetOrCreate('sessionCookie', cookies[0]);
}

这里的处理逻辑是:
● 从响应头中提取Cookie
● 将Cookie保存在应用存储中
● 在后续的请求(如登录)中,会将这个Cookie发送回服务器

  1. 确保Session ID一致的机制 通过这种方式,客户端和服务器可以保持Session ID的一致性:
    ● 服务器在sendMsg方法中生成一个唯一的Session ID
    ● 将这个Session ID作为Cookie的值发送给客户端
    ● 客户端在后续请求(如loginByPhone)中携带这个Cookie
    ● 服务器可以通过Cookie中的Session ID识别和恢复之前的会话上下文
  2. 安全和会话管理的附加措施
    ● 服务器设置了Session的超时时间:session.setMaxInactiveInterval(300),即5分钟
    ● 验证码使用后会立即从Session中移除:session.removeAttribute(“SMS_CODE_” + phone)
    ● 使用HttpOnly和设置路径等方式增加Cookie的安全性

    1. 实际流程示例 完整的流程大致是: a. 用户请求发送验证码 b. 服务器生成验证码并创建Session c. 服务器在响应中发送包含Session ID的Cookie d. 客户端保存这个Cookie e. 用户输入验证码进行登录 f. 客户端在登录请求中携带之前保存的Cookie g. 服务器通过Cookie中的Session ID验证会话和验证码
    通过这种精心设计的机制,应用程序能够在无状态的HTTP协议上实现有状态的会话管理,确保验证码的安全性和登录流程的完整性。
    // 从会话中获取与手机号关联的验证码
Object codeInSession = session.getAttribute("SMS_CODE_" + phone);

这里的关键点是:

  1. session 对象是直接从 HttpSession 获取的
  2. 服务器能获取到 session 对象,意味着 Session ID 已经被正确识别
  3. 通过 getAttribute() 方法,从会话中获取之前存储的验证码
    具体验证流程:
    ● 服务器使用请求中的 Session ID(通过 Cookie 传递)找到对应的 Session
    ● 从 Session 中检索特定手机号的验证码
    ● 将请求中的验证码与 Session 中存储的验证码进行比较
    换句话说,服务器通过 Cookie 中的 Session ID 自动定位到正确的会话,这个过程是框架(如 Spring)和 Servlet 容器自动处理的,开发者不需要手动编写额外的 Session ID 验证代码。

Cookie在这个过程中就像是一个安全的、临时的身份标记,帮助服务器在多个请求中识别和管理同一个用户会话。

在这个程序中,Cookies对于确保Session ID一致性起到了至关重要的作用。:

  1. Session ID的本质 Session ID是一个唯一的标识符,用于在服务器端识别和跟踪特定的用户会话。在这个程序中,Session ID用于关联发送验证码和登录的一系列操作。
  2. Cookies在Session ID一致性中的具体作用 在发送验证码的服务器端代码中,有这样一段关键逻辑:
// 获取或创建Session,并获取Session ID
HttpSession session = request.getSession(true);
String sessionId = session.getId();// 创建JSESSIONID Cookie,用于维持会话状态
Cookie cookie = new Cookie("JSESSIONID", sessionId);
cookie.setPath("/");
cookie.setHttpOnly(true);
response.addCookie(cookie);

这段代码的作用是:
● 创建一个会话(Session)
● 获取这个会话的唯一标识符(Session ID)
● 将Session ID作为Cookie的值发送到客户端

  1. 会话一致性的具体机制 当客户端在后续的登录请求中携带这个Cookie时:
// 从存储中获取保存的cookie
const sessionCookie = AppStorage.Get<string>('sessionCookie');
const requestOptions = this.getRequestOptions(http.RequestMethod.POST, {phone: phone,code: code
});// 如果存在cookie,则添加到请求头中
if (sessionCookie) {requestOptions.header['Cookie'] = sessionCookie;
}

服务器端会通过这个Cookie来识别和恢复之前的会话状态。

  1. 为什么需要这种机制? 在无状态的HTTP协议中,每个请求都是独立的。Cookies提供了一种在多个请求之间维持状态的方法。具体来说:
    ● 发送验证码时,创建一个Session并生成Session ID
    ● 将Session ID通过Cookie发送到客户端
    ● 登录时,客户端将这个Cookie发送回服务器
    ● 服务器通过Cookie中的Session ID找到对应的Session
    ● 验证之前存储在Session中的验证码
  2. 一致性保证的关键步骤
    ● 服务器为每个会话生成唯一的Session ID
    ● 通过Cookie将Session ID传递给客户端
    ● 客户端在后续请求中携带这个Cookie
    ● 服务器根据Cookie中的Session ID恢复会话
  3. 安全性考虑
    ● HttpOnly标志防止客户端脚本访问Cookie
    ● 设置Cookie路径为"/",使其在整个应用可用
    ● Session设置5分钟超时
    ● 验证码使用后立即失效
    通过这种机制,Cookie确保了:
    ● 发送验证码和登录请求属于同一个会话
    ● 验证码只能使用一次
    ● 会话状态在多个请求间保持一致
    想象一下,Cookie就像是一个特殊的身份标签,在用户的整个验证流程中始终跟随并识别用户,确保从发送验证码到最终登录的每一步都能准确地关联起来。

解决Session不一致问题的方法总结:
1.Cookie管理
前端统一管理Cookie存储
每次请求都携带相同的Cookie
在发送验证码时保存Cookie
后续请求复用相同Cookie
2.Session配置
合理配置Session超时时间
设置正确的Cookie属性
统一Session存储方式
配置跨域时允许携带凭证
3.请求处理
使用统一的请求拦截器
确保请求头包含必要信息
正确处理响应头中的Cookie
维护请求的一致性
4.日志记录
记录Session创建和销毁
记录Cookie的变化
记录验证码存储和验证过程
便于问题定位和调试
5.错误处理
统一的Session过期处理
友好的错误提示
完善的异常捕获
合理的重试机制
6. 最佳实践
避免频繁创建新Session
及时清理过期Session
合理使用Session存储
定期检查Session状态
通过以上方法的组合使用,可以有效解决Session不一致的问题。

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

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

相关文章

基于多视角深度学习技术的乳腺X线分类:图神经网络与Transformer架构的研究|文献速递-生成式模型与transformer在医学影像中的应用速递

Title 题目 Mammography classification with multi-view deep learning techniques:Investigating graph and transformer-based architectures 基于多视角深度学习技术的乳腺X线分类&#xff1a;图神经网络与Transformer架构的研究 01 文献速递介绍 乳腺X线检查是乳腺癌…

开发环境服务器 vs 生产环境服务器:开发与生产须分明

【背景】作为开发者&#xff0c;我们在不同的阶段都与两种服务器环境打交道——开发环境服务器和生产环境服务器。虽然听起来名字相似&#xff0c;但它们的职责和工作方式简直是天差地别&#xff01; 不知道朋友们有没有跟我一开始刚了解的时候的一些疑惑&#xff0c;因为刚开始…

Maxscript移动、旋转和缩放

3DMAX对象最常用的三种变换方式是移动、旋转和缩放。本文将详细介绍这些是如何工作的。移动&#xff1a; 使用move函数处理移动&#xff1a; move <object> <vector> 按矢量移动对象&#xff1a; c cone() --c is at 0,0,0 move c [0,1,0] --after this l…

恢复删除的文件:6个免费Windows电脑数据恢复软件

数据恢复软件可帮助您从众多存储设备中恢复损坏或删除的数据。您可以使用这些文件恢复软件来检索文件、文档、视频、图片等。这些应用程序支持多种标准文件格式&#xff0c;如 PNG、RTF、PDF、HTML、JPG、MP3 等。 经过超过 75 小时的研究&#xff0c;我分析了 25 最佳免费数据…

探索 Python 编程调试案例:程序平均值的修复过程

&#x1f49d;&#x1f49d;&#x1f49d;Python 作为一门广泛应用的编程语言&#xff0c;其编程过程并非总是一帆风顺。即使是经验丰富的程序员&#xff0c;也会在代码中遇到各种错误。而调试Python代码过程&#xff0c;也是学习中不可避免的步骤。 今天来看一个简单的案例。…

简单了解一下 Go 语言构建约束?

​构建约束是一种在 Go 语言中控制源文件编译条件的方法&#xff0c;它可以让您指定某些文件只在特定的操作系统、架构、编译器或 Go 版本下编译&#xff0c;而在其他环境中自动忽略。这样可以方便您针对不同的平台或场景编写不同的代码&#xff0c;实现条件编译的功能。 构建…

全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之计数器与累加器实战题目

题目1—三个连续的自然数计算 问题描述&#xff1a; 若有3个连续的自然数&#xff0c;已知第一个自然数为100&#xff0c;请编写程序输出这3个自然数。 输入格式&#xff1a; 无 输出格式&#xff1a; 三行三个整数 输入输出样例&#xff1a; 输入样例 输出样例 无 1…

人工智能大语言模型起源篇(二),从通用语言微调到驾驭LLM

上一篇&#xff1a;《人工智能大语言模型起源篇&#xff08;一&#xff09;&#xff0c;从哪里开始》 &#xff08;5&#xff09;Howard 和 Ruder 于2018年发表的《Universal Language Model Fine-tuning for Text Classification》&#xff0c;https://arxiv.org/abs/1801.06…

基于MATLAB的电力系统潮流计算

为了进行潮流计算&#xff0c;通常可以使用像MATPOWER这样的电力系统分析工具包&#xff0c; 在MATLAB中的MATPOWER工具包中&#xff0c;多个不同的系统模型被定义为测试案例&#xff08;case&#xff09;。这些测试案例用于模拟和分析电力系统中的潮流计算、最优潮流问题、稳…

Unity屏幕截图、区域截图、读取图片、WebGL长截屏并下载到本地jpg

Unity屏幕截图、区域截图、读取图片、WebGL长截屏并下载到本地jpg 一、全屏截图并保存到StreamingAssets路径下 Texture2D screenShot;//保存截取的纹理public Image image; //显示截屏的Imagepublic void Jietu(){StartCoroutine(ScrrenCapture(new Rect(0, 0, Screen.width…

使用 ADB (Android Debug Bridge) 工具来截取 Android 设备的屏幕截图

可以使用 ADB (Android Debug Bridge) 工具来截取 Android 设备的屏幕截图。以下是具体的操作步骤&#xff1a; 1. 连接设备 确保 Android 设备通过 USB 或网络连接到电脑&#xff0c;并运行以下命令检查连接状态&#xff1a; adb devices2. 截取屏幕截图 运行以下命令将设…

TypeScript 的崛起:全面解析与深度洞察

一、背景与起源 &#xff08;一&#xff09;JavaScript 的局限性 类型系统缺失 难以在编码阶段发现类型相关错误&#xff0c;导致运行时错误频发。例如&#xff0c;将字符串误当作数字进行数学运算&#xff0c;可能在运行时才暴露问题。函数参数类型不明确&#xff0c;容易传入…

Moretl无人值守日志采集工具

永久免费: 至Gitee下载 使用教程: Moretl使用说明 用途 定时全量或增量采集工控机,电脑文件或日志. 优势 开箱即用: 解压直接运行.不需额外下载.管理设备: 后台统一管理客户端.无人值守: 客户端自启动,自更新.稳定安全: 架构简单,兼容性好,通过授权控制访问. 架构 技术架…

The Rise and Potential of Large Language ModelBased Agents:A Survey---摘要、背景、引言

题目 基于大语言模型的Agent的兴起与发展前景 论文地址&#xff1a;https://arxiv.org/pdf/2309.07864.pdf 项目地址&#xff1a;https:/github.com/WooooDyy./LLM-Agent–Paper-List 摘要 长期以来&#xff0c;人类一直在追求等同于或超越人类水平的人工智能(A)&#xff0c;…

lc46全排列——回溯

46. 全排列 - 力扣&#xff08;LeetCode&#xff09; 法1&#xff1a;暴力枚举 总共n!种全排列&#xff0c;一一列举出来放入list就行&#xff0c;关键是怎么去枚举呢&#xff1f;那就每次随机取一个&#xff0c;然后删去这个&#xff0c;再从剩下的数组中继续去随机选一个&a…

题目 1761: 学习ASCII码

题目 1761: 学习ASCII码 时间限制: 2s 内存限制: 192MB 提交: 4331 解决: 2415 题目描述 刚开始学C语言&#xff0c;ASCII码可是必须要会的哦&#xff01;那么问题来了&#xff0c;要求你用熟悉的printf输出字符常量 ’ t ’ 的ASCII以及ASCII码值63对应的字符&#xff01; 注…

使用Flink CDC实现 Oracle数据库数据同步的oracle配置操作

使用Flink CDC实现 Oracle数据库数据同步的oracle配置操作&#xff0c;包括开启日志归档和用户授权。 flink官方参考资料&#xff1a; https://nightlies.apache.org/flink/flink-cdc-docs-master/zh/docs/connectors/flink-sources/oracle-cdc/ 操作步骤&#xff1a; 1.启用…

字体子集化实践探索

最近项目rust生成PDF组件printpdf需要内嵌完整字体导致生成的PDF很大&#xff0c;需要做压缩&#xff0c;但是rust的类库allsorts::subset::subset不支持windows&#xff0c;所以做了一些windows下字体子集化的尝试 方案一&#xff1a;node.js做子集化 fontmin 缺点是也需要集…

Spring Boot教程之二十五: 使用 Tomcat 部署项目

Spring Boot – 使用 Tomcat 部署项目 Spring Boot 是一个基于微服务的框架&#xff0c;在其中创建可用于生产的应用程序只需很少的时间。Spring Boot 建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。如今&#xff0c;它正成为开发人员的最爱&#xff0c;因为它是一个…

Vue自定义快捷键做粘贴

静态&#xff1a; export default {data() {return {customContent: 这里是你想要粘贴的自定义内容 // 自定义内容};},mounted() {window.addEventListener(keydown, this.handleKeyDown);},beforeDestroy() {window.removeEventListener(keydown, this.handleKeyDown);},meth…