SpringBoot 实现多个子域共享 cookie

news2025/2/27 21:15:37

SpringBoot 实现多个子域共享 cookie

  • 项目信息
  • cookie 共享
    • 需求
    • 如何实现
  • 环境配置
    • 配置域
    • SpringBoot 配置 https 访问
  • 后端代码
  • 验证
  • 验证后端解析 cookie

项目信息

使用SpringBoot web框架,版本号 2.7.10

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

cookie 共享

需求

现在有两个域名 dev.scd.com.cn,test.scd.com.cn 的服务,登录验证之后,服务器写入 cookie 到响应头,用户只需要登录一次,访问任意一个域都携带cookie 信息

如何实现

后端服务写入cookie 指定domain为二级域名 .scd.com.cn
cookie基础知识

环境配置

配置域

本地模拟多个域的环境,需要在host 上配置域名,增加如下配置

127.0.0.1 dev.scd.com.cn
127.0.0.1 test.scd.com.cn

SpringBoot 配置 https 访问

application.properties 文件增加如下配置

# https
server.ssl.key-store=F:/keytool/https.p12
server.ssl.key-alias=tomcathttps
server.ssl.key-store-password=shootercheng

执行jdk 自带的命令行工具 keytool 生成证书

keytool -genkey -alias tomcathttps -keyalg RSA -keysize 4096 -keystore https.p12 -validity 365

参考地址
SpringBoot Https配置

后端代码

在 httpResponse 响应中增加 cookie

package com.scd.cookie.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;

/**
 * @author James
 * @date 2023/4/8
 */
@RestController
@RequestMapping(value = "/cookie")
public class CookieController {
    private static final Logger LOGGER = LoggerFactory.getLogger(CookieController.class);

    private static final String COOKIE_DOMAIN = ".scd.com.cn";

    private static final int COOKIE_MAX_AGE_DAY = 7;

    private static final String COOKIE_KEY = "test_key";

    @GetMapping("/set")
    public String cookieSet(HttpServletResponse response) {
        Cookie cookie = new Cookie(COOKIE_KEY, "scd");
        cookie.setDomain(COOKIE_DOMAIN);
        cookie.setHttpOnly(true);
        cookie.setPath("/");
        cookie.setSecure(true);
        cookie.setMaxAge((int) Duration.ofDays(COOKIE_MAX_AGE_DAY).getSeconds());
        response.addCookie(cookie);
        return "OK";
    }

    @GetMapping("/get")
    public String cookieGet(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if (COOKIE_KEY.equals(cookie.getName())) {
                LOGGER.info("cookie name {} value {}", COOKIE_KEY, cookie.getValue());
            }
            return cookie.getValue();
        }
        return "OK";
    }
}

启动服务,访问 https://dev.scd.com.cn:8080/cookie/set
在这里插入图片描述
后端服务报错信息如下 An invalid domain [.scd.com.cn] was specified for this cookie

java.lang.IllegalArgumentException: An invalid domain [.scd.com.cn] was specified for this cookie
	at org.apache.tomcat.util.http.Rfc6265CookieProcessor.validateDomain(Rfc6265CookieProcessor.java:218) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.tomcat.util.http.Rfc6265CookieProcessor.generateHeader(Rfc6265CookieProcessor.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.connector.Response.generateCookieString(Response.java:970) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.connector.Response.addCookie(Response.java:923) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.connector.ResponseFacade.addCookie(ResponseFacade.java:314) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at javax.servlet.http.HttpServletResponseWrapper.addCookie(HttpServletResponseWrapper.java:57) ~[tomcat-embed-core-9.0.73.jar:4.0.FR]
	at com.scd.cookie.controller.CookieController.cookieSet(CookieController.java:39) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_77]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_77]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_77]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_77]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.26.jar:5.3.26]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.26.jar:5.3.26]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:502) ~[tomcat-embed-core-9.0.73.jar:4.0.FR]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.26.jar:5.3.26]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:596) ~[tomcat-embed-core-9.0.73.jar:4.0.FR]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.26.jar:5.3.26]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.26.jar:5.3.26]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.26.jar:5.3.26]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.26.jar:5.3.26]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142) ~[spring-session-core-2.7.1.jar:2.7.1]
	at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82) ~[spring-session-core-2.7.1.jar:2.7.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.26.jar:5.3.26]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.26.jar:5.3.26]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:389) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.73.jar:9.0.73]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.73.jar:9.0.73]

根据堆栈信息,发现报错的方法在校验域名时候产生的org.apache.tomcat.util.http.Rfc6265CookieProcessor#generateHeader(javax.servlet.http.Cookie, javax.servlet.http.HttpServletRequest)
org.apache.tomcat.util.http.Rfc6265CookieProcessor#validateDomain

	private void validateDomain(String domain) {
        int i = 0;
        int prev = true;
        int cur = -1;

        for(char[] chars = domain.toCharArray(); i < chars.length; ++i) {
            int prev = cur;
            cur = chars[i];
            if (!domainValid.get(cur)) {
                throw new IllegalArgumentException(sm.getString("rfc6265CookieProcessor.invalidDomain", new Object[]{domain}));
            }

            if ((prev == 46 || prev == -1) && (cur == 46 || cur == 45)) {
                throw new IllegalArgumentException(sm.getString("rfc6265CookieProcessor.invalidDomain", new Object[]{domain}));
            }

            if (prev == 45 && cur == 46) {
                throw new IllegalArgumentException(sm.getString("rfc6265CookieProcessor.invalidDomain", new Object[]{domain}));
            }
        }

如何绕过校验了,跟踪代码发现最终添加 cookie 的代码方法为
org.apache.catalina.connector.Response#addCookie
在这里插入图片描述
org.apache.catalina.connector.Response#addHeader(java.lang.String, java.lang.String, java.nio.charset.Charset)
在这里插入图片描述
最终是转换为字符串设置到 “Set-Cookie” 字符串的,可以使用如下方法直接设置

ResponseCookie cookie = ResponseCookie.from(COOKIE_KEY, "scd")
                .httpOnly(true)
                .secure(true)
                .domain(COOKIE_DOMAIN)
                .path("/")
                .sameSite("Lax")
                .maxAge(Duration.ofDays(COOKIE_MAX_AGE_DAY))
                .build();
        response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());

这样就绕过校验了。调整之后的代码如下:

package com.scd.cookie.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;

/**
 * @author James
 * @date 2023/4/8
 */
@RestController
@RequestMapping(value = "/cookie")
public class CookieController {
    private static final Logger LOGGER = LoggerFactory.getLogger(CookieController.class);

    private static final String COOKIE_DOMAIN = ".scd.com.cn";

    private static final int COOKIE_MAX_AGE_DAY = 7;

    private static final String COOKIE_KEY = "test_key";

    @GetMapping("/set")
    public String cookieSet(HttpServletResponse response) {
//        Cookie cookie = new Cookie(COOKIE_KEY, "scd");
//        cookie.setDomain(COOKIE_DOMAIN);
//        cookie.setHttpOnly(true);
//        cookie.setPath("/");
//        cookie.setSecure(true);
//        cookie.setMaxAge((int) Duration.ofDays(COOKIE_MAX_AGE_DAY).getSeconds());
//        response.addCookie(cookie);
        ResponseCookie cookie = ResponseCookie.from(COOKIE_KEY, "scd")
                .httpOnly(true)
                .secure(true)
                .domain(COOKIE_DOMAIN)
                .path("/")
                .sameSite("Lax")
                .maxAge(Duration.ofDays(COOKIE_MAX_AGE_DAY))
                .build();
        response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
        return "OK";
    }

    @GetMapping("/get")
    public String cookieGet(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if (COOKIE_KEY.equals(cookie.getName())) {
                LOGGER.info("cookie name {} value {}", COOKIE_KEY, cookie.getValue());
            }
            return cookie.getValue();
        }
        return "OK";
    }
}

验证

重启服务,之后访问 https://dev.scd.com.cn:8080/cookie/set,发现响应头设置cookie 成功
在这里插入图片描述
再次请求这个地址,请求头也携带了 cookie
在这里插入图片描述
访问 https://test.scd.com.cn:8080/cookie/set, 请求头也会携带cookie
在这里插入图片描述

验证后端解析 cookie

  1. 使用无痕模式,先访问 https://dev.scd.com.cn:8080/cookie/set
  2. 再访问地址 https://test.scd.com.cn:8080/cookie/get
    在这里插入图片描述
    在这里插入图片描述
    可以看到 cookie 在多个子域的请求都可以携带

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

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

相关文章

Ambari 操作HDP组件迁移

目录 ​ 一、集群信息 1.1 HDP版本信息 1.2 服务器信息 二、服务迁移操作 一、集群信息 1.1 HDP版本信息 1.2 服务器信息 角色 IP 组件 hdp103 192.168.2.152 NameNode hdp104 192.168.2.153 新 NameNode hdp105 192.168.2.154 旧NameNode 二、服务迁移操作 我…

6、苹果签名原理

一、iOS应用签名原理 代码签名双层代码签名描述文件 1.1 代码签名 代码签名是对可执行文件或脚本进行数字签名,用来确认软件在签名后未被修改或损坏的措施. 和数字签名原理一样,只不过签名的数据是代码. 1.1.1 简单的代码签名 - 在iOS出来之前,以前的主流操作系统(Mac/Win…

自动控制原理模拟卷5

自动控制原理模拟题五 Question1 液位自动控制系统原理示意图如下图所示,在任意情况下,希望液面高度 c c c维持不变,说明系统工作原理并画出系统方块图。 解: 当电位器电刷位于中点位置时,电动机不动,控制阀门有一定的开度,使水箱中流入水量与流出水量相等,从而液面保…

C6678-缓存和内存

C6678-缓存和内存 全局内存映射扩展内存控制器&#xff08;XMC&#xff09;-MPAX内存保护与地址扩展使用例程缓存 全局内存映射 扩展内存控制器&#xff08;XMC&#xff09;-MPAX内存保护与地址扩展 每个C66x核心都具有相同大小的L1和L2缓存&#xff0c;并且可配置为普通内存使…

【微信小程序-原生开发】实用教程21 - 分包

分包的流程 当微信小程序主包大小超过2M时&#xff0c;则需要对微信小程序进行分包&#xff0c;方法如下&#xff1a; 1. 转移页面文件 在项目根目录下&#xff0c;新建文件夹 package1 &#xff08;即自定义的分包名为 package1 &#xff09;文件夹 package1 内新建文件夹 p…

C嘎嘎~~【初识C++ 中篇】

初识C 中篇 1.缺省参数2.函数重载2.1函数重载的概念2.2函数重载的原理 3.auto关键字3.1类型别名思考&#xff08;typedef&#xff09;3.2auto的简介3.3auto的使用规则3.4 auto不能推导的场景 4.基于范围的for循环4.1范围for的语法4.2范围for的使用条件 1.缺省参数 缺省参数是 声…

02_2440soc_内存地址映射和选择,寄存器地址的选择

大多数arm芯片从0开始启动 nand_flash控制器外接nand_flash 256M cpu直接接nor_flash 2M nor_flash启动时候 nor_flash上面烧了bin文件 nor_flash基地址为0 cpu直接读nor上的第一个指令(前4字节),执行 cpu继续读取其他指令 执行 如果使用nor启动,片内的sram的地址就是 0x4000…

实战:内存分配与回收策略

java技术体系的自动内存管理&#xff0c;最根本的目标是自动化地解决两个问题&#xff1a;自动给对象分配内存以及自动回收分配给对象的内存。 1 前置知识 1.1 Minor GC 与Full GC Minor GC: 新生代GC&#xff0c;是指发生新生代的垃圾收集动作。Minor GC非常频繁&#xff0…

mac m1安装stable-diffusion

安装stable-diffusion 安装Homebrew安装python及其他依赖下载model安装Stable-Diffusion-WebUI执行./webui.sh作图http://127.0.0.1:7860 安装Homebrew 查询是否安装 brew -v安装 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.…

14.网络爬虫—selenium详讲

网络爬虫—selenium详讲 一selenium简介Selenium 的优点 二安装模块三设置浏览器驱动确认版本&#xff1a;查找对应驱动下载驱动 四使用模块selenium选取元素方法selenium嵌套页面元素定位selenium网页下拉selenium下拉表选择selenium行为链selenium等待 五错误解决方案六结束语…

[oeasy]python0135_python_语义分析_ast_抽象语法树_abstract_syntax_tree

语义分析_抽象语法树_反汇编 回忆 上次回顾了一下历史 python 是如何从无到有的看到 Guido 长期的坚持和努力 python究竟是如何理解 print(“hello”)的&#xff1f;这些ascii字母如何被组织起来执行&#xff1f; 纯文本 首先编写Guido的简历 print("1982------Gui…

LeetCode刷题集(五)(LeetCode1.两数之和)

学习目标&#xff1a; 掌握LeetCode第一题两数之和 学习内容&#xff1a; LeetCode第一题两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会…

ASEMI代理AD9951YSVZ原装ADI车规级AD9951YSVZ

编辑&#xff1a;ll ASEMI代理AD9951YSVZ原装ADI车规级AD9951YSVZ 型号&#xff1a;AD9951YSVZ 品牌&#xff1a;ADI/亚德诺 封装&#xff1a;HTQFP-48 批号&#xff1a;2023 引脚数量&#xff1a;48 安装类型&#xff1a;表面贴装型 AD9951YSVZ汽车芯片 特征 400 MS…

shell编程实例 — 实现4G开机自动运行,实时监测,断线自动重拨》

shell编程实例 — 实现4G开机自动运行&#xff0c;实时监测&#xff0c;断线自动重拨》 往事只能回味味道 于 2021-11-20 02:30:53 发布831 收藏 1 分类专栏&#xff1a; shell 文章标签&#xff1a; ubuntu linux udp 版权 ​编辑shell专栏收录该内容 3 篇文章0 订阅 订阅…

Mysql日志系统-InnoDB引擎层

一、redo log日志 接下来的两个日志&#xff0c;是innodb为解决不同问题而引出的两类日志文件。 redo log&#xff08;重做日志&#xff09;的设计主要是为了防止因系统崩溃而导致的数据丢失&#xff0c;其实解决因系统崩溃导致数据丢失的思路如下&#xff1a; 1、每次提交事务…

SpringBoot解决用户重复提交订单(方式二:通过Redis实现)

文章目录 前言1、方案实践1.1、引入Redis依赖1.2、添加Redis环境配置1.3、编写获取请求唯一ID的接口&#xff0c;同时将唯一ID存入redis 1.4、编写服务验证逻辑&#xff0c;通过 aop 代理方式实现1.5、在相关的业务接口上&#xff0c;增加SubmitToken注解即可 2、小结 前言 在…

华润数科、京东科技、京彩未来携手共进,求解“科技×产业”最大值

4月18日&#xff0c;华润数科、京东科技、京彩未来三方代表在中国深圳签署三方合作协议。面向产业数字化的市场机遇&#xff0c;华润数科物联网事业部总经理刘楚明先生表示&#xff0c;华润数科作为华润集团重点培育的数字科技业务单元&#xff0c;基于技术优势及资源整合能力&…

在SAP中使用QUERY

在SAP中使用QUERY 一、SAP query介绍 ​ QUERY是SAP提供的方便无编程基础用户的报表工具&#xff0c;使用图形化的界面&#xff0c;让用户托托拽拽就能轻松完成报表编写。我们可以将Query理解成QuickView的高级版本&#xff0c;它在QuickView的基础上增加的功能有&#xff1a…

一、摄影基础课

目录 第一章 控制曝光1. 曝光及曝光三要素1.1 曝光1.2 光圈1.3 快门1.4 感光度&#xff08;ISO&#xff09;1.5 曝光三要素之间的关系 2. 曝光模式3. 测光模式4. 曝光补偿 第二章 控制对焦1. 对焦区域2. 对焦模式 第三章 控制色彩 第一章 控制曝光 1. 曝光及曝光三要素 1.1 曝…

尚硅谷大数据技术Zookeeper教程-笔记01【Zookeeper(入门、本地安装、集群操作)】

视频地址&#xff1a;【尚硅谷】大数据技术之Zookeeper 3.5.7版本教程_哔哩哔哩_bilibili 尚硅谷大数据技术Zookeeper教程-笔记01【Zookeeper(入门、本地安装、集群操作)】尚硅谷大数据技术Zookeeper教程-笔记02【服务器动态上下线监听案例、ZooKeeper分布式锁案例、企业面试真…