当前位置: 首页 > news >正文

java - SpringBoot3.x接入Security6.x实现JWT认证

java - SpringBoot3.x接入Security6.x实现JWT认证

文章目录

  • java - SpringBoot3.x接入Security6.x实现JWT认证
  • 一、引言
  • 二、环境
  • 三、Maven依赖
  • 四、认识JWT
    • 1. JWT组成
  • 五、认识Security6.x
    • 1. 和旧版本的区别(Security5.7以前的版本)
    • 2. Security6.x的默认筛选器
    • 3. 注册SecurityFilterChain
  • 六、基于OncePerRequestFilter自定义JWT认证筛选器
    • 1. 标记认证成功
  • 七、遇到的问题
    • 1. 加入Security6后,一直出现登录页
    • 2. 配置完匿名访问的URL后,仍然执行自定的筛选器
  • 八、完成JWT认证的主要代码
    • 1. JwtUtil
    • 2. JwtTokenFilter
    • 3. SecuritConfig
  • 总结

一、引言

SpringBoot3.x的安全默认依赖Security6.x,Security6.x于Security5.7以前的配置有了很大区别。我们将深入探讨这两个版本之间的差异,以及它们如何影响现代Web应用的安全架构。特别是,我们将重点分析JWT(JSON Web Tokens)过滤器的工作原理,以及它是如何与匿名访问相结合,为应用提供更加灵活的安全控制。

二、环境

  • JDK 17
  • SpringBoot 3.2
  • Security 6.3

三、Maven依赖

<!-- Security安全 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>3.2.2</version>
</dependency>
<!-- jwt接口认证 -->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version>
</dependency>      

四、认识JWT

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

1. JWT组成

JSON Web Token由三部分组成,它们之间用圆点(.)连接,一个典型的JWT看起来是这个样子的:

  • 第一部分:header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等),然后,用Base64对这个JSON编码就得到JWT的第一部分。
  • 第二部分:payload它包含声明(要求),声明是关于实体(通常是用户)和其他数据的声明。
  • 第三部分:签名是用于验证消息在传递过程中有没有被更改,并且对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。

注意:不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

{alg: "RS256"
}.
{
//存储自定义的用户信息,属性可以自定扩充login_name: "admin",user_id: "xxxxx",...
}.
[signature]
  • 请求header应该是这样的:Authorization: Bearer

五、认识Security6.x

1. 和旧版本的区别(Security5.7以前的版本)

SpringBoot3中默认Security升级到了6.x写法上发生了很大的变化,最显著的变化之一就是对WebSecurityConfigurerAdapter类的使用方式的改变。这个类在 Spring Security 中被广泛用于自定义安全配置。以下是主要的差异和写法上的变化:

  • 废弃WebSecurityConfigurerAdapter:

在Security5.x 版本中,WebSecurityConfigurerAdapter 是实现安全配置的常用方法。用户通过继承这个类,并覆盖其方法来自定义安全配置。到了 Spring Security 6.x,WebSecurityConfigurerAdapter 被标记为过时(deprecated),意味着它可能在未来的版本中被移除。这一变化是为了推动使用更现代的配置方法,即使用组件式配置。

  • 新版本建议使用组件式配置:

在 Spring Security 6.x 中,推荐使用组件式配置。这意味着你可以创建一个配置类,该类不再需要继承 WebSecurityConfigurerAdapter。
你可以直接定义一个或多个 SecurityFilterChain Bean来配置安全规则。这种方式更加灵活,并且与 Spring Framework 的整体风格更加一致。

2. Security6.x的默认筛选器

支持的所有筛选器在spring-security-config-6.2.1.jar包的org.springframework.security.config.annotation.web.builders.FilterOrderRegistration类的构造函数中定义,并确定了执行顺序。

FilterOrderRegistration() {Step order = new Step(INITIAL_ORDER, ORDER_STEP);put(DisableEncodeUrlFilter.class, order.next());put(ForceEagerSessionCreationFilter.class, order.next());put(ChannelProcessingFilter.class, order.next());order.next(); // gh-8105put(WebAsyncManagerIntegrationFilter.class, order.next());put(SecurityContextHolderFilter.class, order.next());put(SecurityContextPersistenceFilter.class, order.next());put(HeaderWriterFilter.class, order.next());put(CorsFilter.class, order.next());put(CsrfFilter.class, order.next());put(LogoutFilter.class, order.next());this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",order.next());this.filterToOrder.put("org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter",order.next());put(X509AuthenticationFilter.class, order.next());put(AbstractPreAuthenticatedProcessingFilter.class, order.next());this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",order.next());this.filterToOrder.put("org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",order.next());put(UsernamePasswordAuthenticationFilter.class, order.next());order.next(); // gh-8105put(DefaultLoginPageGeneratingFilter.class, order.next());put(DefaultLogoutPageGeneratingFilter.class, order.next());put(ConcurrentSessionFilter.class, order.next());put(DigestAuthenticationFilter.class, order.next());this.filterToOrder.put("org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter",order.next());put(BasicAuthenticationFilter.class, order.next());put(RequestCacheAwareFilter.class, order.next());put(SecurityContextHolderAwareRequestFilter.class, order.next());put(JaasApiIntegrationFilter.class, order.next());put(RememberMeAuthenticationFilter.class, order.next());put(AnonymousAuthenticationFilter.class, order.next());this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",order.next());put(SessionManagementFilter.class, order.next());put(ExceptionTranslationFilter.class, order.next());put(FilterSecurityInterceptor.class, order.next());put(AuthorizationFilter.class, order.next());put(SwitchUserFilter.class, order.next());
}

3. 注册SecurityFilterChain

    private final String[] permitUrlArr = new String[]{"xxx"};/*** 配置Spring Security安全链。*/@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//初始化jwt过滤器,并设置jwt公钥var jwtTokenFilter = new JwtTokenFilter();//Security6.x关闭默认登录页httpSecurity.removeConfigurers(DefaultLoginPageConfigurer.class);logger.info("注册JWT认证SecurityFilterChain");var chain = httpSecurity// 自定义权限拦截规则.authorizeHttpRequests((requests) -> {//requests.anyRequest().permitAll(); //放行所有请求!!!//允许匿名访问requests//自定可匿名访问地址,放到permitAllUrl中即可.requestMatchers(permitUrlArr).permitAll()//除上面声明的可匿名访问地址,其它所有请求全部需要进行认证.anyRequest().authenticated();})// 禁用HTTP响应标头.headers(headersCustomizer -> {headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());})//会话设为无状态,基于token,所以不需要session.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))//添加自定义的JWT认证筛选器,验证header中jwt有效性,将插入到UsernamePasswordAuthenticationFilter之前 .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)//禁用表单登录.formLogin(formLogin -> formLogin.disable())//禁用httpBasic登录.httpBasic(httpBasic -> httpBasic.disable())//禁用rememberMe.rememberMe(rememberMe -> rememberMe.disable())// 禁用CSRF,因为不使用session.csrf(csrf -> csrf.disable())//允许跨域请求.cors(Customizer.withDefaults()).build();return chain;}

六、基于OncePerRequestFilter自定义JWT认证筛选器

使用OncePerRequestFilter的优点是,能保证一个请求只过一次筛选器。可以在filter中实现对jwt的校验,验证成功后需要对Security上下文进行标注。标记认证已经通过,这点非常重要。如果认证完了不标注,后边的过滤器还是认为未认证导致无权限失败。

1. 标记认证成功

//接入Spring Security6.x上下文,标记为已认证状态
JwtAuthenticationToken jwtToken = new JwtAuthenticationToken(null);
jwtToken.setAuthenticated(true); //标记认证通过
SecurityContextHolder.getContext().setAuthentication(jwtToken);

七、遇到的问题

1. 加入Security6后,一直出现登录页

关闭默认登录页有两个设置可以完成,可以删除DefaultLoginPageConfigurer类的加载,或者调用formLogin()函数,具体如下:

    @Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//Security6.x关闭默认登录页httpSecurity.removeConfigurers(DefaultLoginPageConfigurer.class);var chain = httpSecurity//禁用表单登录.formLogin(formLogin -> formLogin.disable()).build();return chain;}

2. 配置完匿名访问的URL后,仍然执行自定的筛选器

如果出现配置完匿名访问的URL后,仍然执行自定的筛选器,的问题。那原因就在于这个自定义筛选器上了,
只通过requests.requestMatchers(…).permitAll(); 配置的匿名访问只能对默认筛选器起效,如果想
对自定义删除器起效,还需要构建WebSecurityCustomizer Bean对象,基于匿名函数配置要匿名访问的地址。
一下是官网推荐的一个写法,这里建议把两个位置,配置的匿名访问地址,使用一个公共数组进行管理,这样
能保证两个位置配置的一致性。

    /** 其它不需要认证的地址 */private final String[] permitUrlArr = new String[]{"/login","/error"//静态资源,"/static/**.ico","/static/**.js","/static/**.css"//匹配springdoc,"/doc.html","/webjars/**"//匹配swagger路径(默认), "/swagger-ui.html", "/swagger-ui/index.html", "/v3/api-docs/**", "/swagger-ui/**"//监控检测, "/actuator/**"};@Beanpublic WebSecurityCustomizer ignoringCustomize(){return (web) -> web.ignoring().requestMatchers(permitUrlArr);}@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//初始化jwt过滤器,并设置jwt公钥var jwtTokenFilter = new JwtTokenFilter();//Security6.x关闭默认登录页httpSecurity.removeConfigurers(DefaultLoginPageConfigurer.class);logger.info("注册JWT认证SecurityFilterChain");var chain = httpSecurity// 自定义权限拦截规则.authorizeHttpRequests((requests) -> {//允许匿名访问requests//自定可匿名访问地址,放到permitAllUrl中即可.requestMatchers(permitUrlArr).permitAll()//除上面声明的可匿名访问地址,其它所有请求全部需要进行认证.anyRequest().authenticated();}).build();return chain;}                    

八、完成JWT认证的主要代码

目前是对已有jwt的认证,下发的jwt是基于RSA加密的内容,需要使用公钥进行解密,公钥一般配置在yml文件里。关键逻辑设计3部分,SecuritConfig、JwtTokenFilter、JwtUtil。

1. JwtUtil

公钥是统一认证中心下发的,目前写在yml中,格式如下:

jwt.keyValue: |-----BEGIN PUBLIC KEY-----xxxxxxxx-----END PUBLIC KEY-----

JwtUtil类提供了验证方法,出于性能考虑使用了单例模式,验证器只需要实例化一次。

public class JwtUtil {private static JwtUtil instance = new JwtUtil();private static JWTVerifier jwtVerifier;//配置文件中公钥的key值private static final String jwtPublicKeyConfig="jwt.keyValue";private JwtUtil()  {}/*** 基于固定配置文件的公钥初始化JWT验证器* @return*/public static JwtUtil getInstance(){if (jwtVerifier == null){String publicKey = SpringUtil.getConfig(jwtPublicKeyConfig);return getInstance(publicKey);}return instance;}/*** 基于自定义公钥初始化JWT验证器* @return*/public static JwtUtil getInstance(String publicKey) {if (jwtVerifier == null){initVerifier(publicKey);}return instance;}// 静态的初始化函数private static synchronized void initVerifier(String publicKey) {if (jwtVerifier != null)return;//替换为实际的Base64编码的RSA公钥字符串String publicKeyStr = publicKey.replaceAll("\\s", "") // 去除所有空白字符,包括换行符.replace("-----BEGINPUBLICKEY-----", "").replace("-----ENDPUBLICKEY-----", "");// 将Base64编码的公钥字符串转换为PublicKey对象byte[] encodedPublicKey = Base64.getDecoder().decode(publicKeyStr);X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedPublicKey);KeyFactory keyFactory = null;try {keyFactory = KeyFactory.getInstance("RSA");PublicKey pubKey = keyFactory.generatePublic(keySpec);// 使用公钥创建一个Algorithm对象,用于验证token的签名Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) pubKey, null);// 解析和验证tokenjwtVerifier = JWT.require(algorithm).build();} catch (NoSuchAlgorithmException e) {throw new RuntimeException(e);} catch (InvalidKeySpecException e) {throw new RuntimeException(e);}catch (Exception e){throw new RuntimeException(e);}}/*** 解析和验证JWT token。** @param token JWT token字符串* @return 解码后的JWT对象* @throws Exception 如果解析或验证失败,抛出异常*/public DecodedJWT verifyToken(String token) {return jwtVerifier.verify(token);}
}

2. JwtTokenFilter

该类是校验的主要逻辑,完成了jwt校验、已认证的标注。

public class JwtTokenFilter extends OncePerRequestFilter {private static Logger logger = LoggerFactory.getLogger(JwtTokenFilter.class);private JwtUtil jwtUtil;//获取yml中的配置public String getConfig(String configKey) {var bean = applicationContext.getBean(Environment.class);var val = bean.getProperty(configKey);return val;}public JwtTokenFilter() throws ServletException {String jwtPubliKey = getConfig("jwt.keyValue");initTokenFilter(jwtPubliKey);}public JwtTokenFilter(String jwtPubliKey) throws ServletException {initTokenFilter(jwtPubliKey);}@Overrideprotected void initFilterBean() throws ServletException {}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {var pass = doTokenFilter(request,response,filterChain);if(!pass){return;}filterChain.doFilter(request,response);}/*** 初始化Token过滤器。* @throws ServletException 如果在初始化过程中发生错误,则抛出ServletException异常*/public void  initTokenFilter(String publicKey) throws ServletException {logger.info("初始化TokenFilter");if(StringUtils.isBlank(publicKey)){throw new ServletException("jwtPublicKey is null");}logger.info("jwtPublicKey:{}",publicKey);jwtUtil = JwtUtil.getInstance(publicKey);logger.info("初始化JwtUtil完成");}protected Boolean doTokenFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;// 从请求头中获取tokenString token = request.getHeader("Authorization");if(StringUtils.isBlank(token)){logger.info("jwt token为空,{} {}",request.getMethod(),request.getRequestURI());// 验证失败,返回401状态码response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token");return false;}// 假设token是以"Bearer "开头的,需要去掉这个前缀if (token.startsWith("Bearer")) {token = token.replaceAll("Bearer\s+","");}logger.debug(request.getRequestURI());try {// 调用JwtUtils进行token验证DecodedJWT jwtDecode = jwtUtil.verifyToken(token);//接入Spring Security6.x上下文,标记为已认证状态JwtAuthenticationToken jwtToken = new JwtAuthenticationToken(null);jwtToken.setAuthenticated(true);SecurityContextHolder.getContext().setAuthentication(jwtToken);//将登录信息写入spring security上下文} catch (JWTVerificationException ex) {logger.info("jwt token 非法");response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "非法token:"+ex.getMessage());return false;} catch (Exception ex) {throw ex;}logger.debug("token验证通过");return true;}public static class JwtAuthenticationToken extends AbstractAuthenticationToken {private User userInfo;public JwtAuthenticationToken(User user) {super(null);this.userInfo =user;}@Overridepublic User getPrincipal() {return userInfo;}@Overridepublic Object getCredentials() {throw new UnsupportedOperationException();}@Overridepublic boolean implies(Subject subject) {return super.implies(subject);}}}

3. SecuritConfig

该类完成了对需要匿名访问的地址的配置,还有自定义filter的注入。

@Configuration
public class SecurityConfig {private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);/** 其它不需要认证的地址 */private final String[] permitUrlArr = new String[]{"/login","/error"//静态资源,"/static/**.ico","/static/**.js","/static/**.css"//匹配springdoc,"/doc.html","/webjars/**"//匹配swagger路径(默认), "/swagger-ui.html", "/swagger-ui/index.html", "/v3/api-docs/**", "/swagger-ui/**"//监控检测, "/actuator/**"};@Beanpublic WebSecurityCustomizer ignoringCustomize(){return (web) -> web.ignoring().requestMatchers(permitUrlArr);}@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//初始化jwt过滤器,并设置jwt公钥var jwtTokenFilter = new JwtTokenFilter();//Security6.x关闭默认登录页httpSecurity.removeConfigurers(DefaultLoginPageConfigurer.class);logger.info("注册JWT认证SecurityFilterChain");var chain = httpSecurity// 自定义权限拦截规则.authorizeHttpRequests((requests) -> {//requests.anyRequest().permitAll(); //放行所有请求!!!//允许匿名访问requests//自定可匿名访问地址,放到permitAllUrl中即可.requestMatchers(permitUrlArr).permitAll()//除上面声明的可匿名访问地址,其它所有请求全部需要进行认证.anyRequest().authenticated();})// 禁用HTTP响应标头.headers(headersCustomizer -> {headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());})//会话设为无状态,基于token,所以不需要session.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))//添加自定义的JWT认证筛选器,验证header中jwt有效性,将插入到UsernamePasswordAuthenticationFilter之前 .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)//禁用表单登录.formLogin(formLogin -> formLogin.disable())//禁用httpBasic登录.httpBasic(httpBasic -> httpBasic.disable())//禁用rememberMe.rememberMe(rememberMe -> rememberMe.disable())// 禁用CSRF,因为不使用session.csrf(csrf -> csrf.disable())//允许跨域请求.cors(Customizer.withDefaults()).build();return chain;}@Beanpublic FilterRegistrationBean disableSpringBootErrorFilter(ErrorPageFilter filter){FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(filter);filterRegistrationBean.setEnabled(false);return filterRegistrationBean;}
}

总结

以上支持介绍了对于已有JWT统一认证系统的接入(JWT解析和认证),不涉及JWT生成和管理相关内容。
目前的用户信息是基于JWT动态解析的,所以暂时没有基于AbstractAuthenticationToken在Security上下文中存放用户信息,JwtAuthenticationToken已经支持自定义用户信息的存储,只需要按需传入即可。基于Security上下文获取用户信息使用SecurityContextHolder.getContext().getAuthentication().getPrincipal();方法。

相关文章:

java - SpringBoot3.x接入Security6.x实现JWT认证

java - SpringBoot3.x接入Security6.x实现JWT认证 文章目录 java - SpringBoot3.x接入Security6.x实现JWT认证一、引言二、环境三、Maven依赖四、认识JWT1. JWT组成 五、认识Security6.x1. 和旧版本的区别&#xff08;Security5.7以前的版本&#xff09;2. Security6.x的默认筛…...

【每日学点鸿蒙知识】无障碍、getLastLocation、蓝牙问题、卡片大小、关系型数据库等

1、是否有类似无障碍辅助相关的API&#xff1f; 场景描述&#xff1a;锁机app&#xff0c;需要通过无障碍能力辅助检测当前正在打开的app&#xff0c;以及模拟用户操作&#xff0c; 关闭用户想要屏蔽的app 可参考&#xff1a;https://developer.huawei.com/consumer/cn/doc/h…...

[Linux] 服务器CPU信息

&#xff08;1&#xff09;查看CPU信息&#xff08;型号&#xff09; cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c输出&#xff1a;可以看到有128个虚拟CPU核心&#xff0c;型号是后面一串 128 Intel(R) Xeon(R) Platinum 8336C CPU 2.30GHz&#xff08;2&…...

MySQL——数据类型

一、常见的数据类型及分类 其中上述的数值类型包含了整形和浮点型&#xff0c;文本、二进制类型主要是字符串类型。 整数类型&#xff08;Integer Types&#xff09;&#xff1a; TINYINT&#xff1a;范围为-128到127或0到255&#xff08;无符号&#xff09;&#xff0c;用于…...

《AI赋能自由职业:开启竞争力提升新征程》

在当今数字化时代&#xff0c;AI技术为自由职业者带来了前所未有的机遇&#xff0c;使其能够在激烈的市场竞争中脱颖而出。以下是自由职业者借助AI提升自身竞争力的几种方法。 利用AI优化工作流程&#xff0c;提高效率 自动化任务处理&#xff1a;自由职业者可以借助自动化工具…...

Excel转Json编辑器工具

功能说明&#xff1a;根据 .xlsx 文件生成对应的 JSON 文件&#xff0c;并自动创建脚本 注意事项 Excel 读取依赖 本功能依赖 EPPlus 库&#xff0c;只能读取 .xlsx 文件。请确保将该脚本放置在 Assets 目录下的 Editor 文件夹中。同时&#xff0c;在 Editor 下再创建一个 Exc…...

创建型设计模式、结构型设计模式与行为型设计模式 上下文任务通用方案 设计模式 大全

设计模式&#xff08;Design Pattern&#xff09;是一种面向对象编程的思想&#xff0c;分为创建型模式、结构型模式与行为型模式三大类&#xff0c;它们提供了在特定上下文中解决常见任务的通用方案&#xff0c;旨在让程序&#xff08;软件&#xff09;具有更好的特点&#xf…...

Mac 环境 VVenC 编译与编码命令行工具使用教程

VVenC VVenC 是一个开源的高效视频编码器&#xff0c;专门用于支持 H.266/VVC (Versatile Video Coding) 标准的编码。H.266/VVC 是继 HEVC (H.265) 之后的新一代视频编码标准&#xff0c;主要目的是提供比 HEVC 更高的压缩效率&#xff0c;同时保持或提高视频质量。H.266/VVC…...

如何在 Ubuntu 22.04 上部署 Nginx 并优化以应对高流量网站教程

简介 本教程将教你如何优化 Nginx&#xff0c;使其能够高效地处理高流量网站。 Nginx 是一个强大且高性能的 Web 服务器&#xff0c;以其高效处理大量并发连接的能力而闻名&#xff0c;这使得它成为高流量网站的流行选择。 正确优化 Nginx 可以显著提高服务器的性能&#xff0…...

springcloud各个组件介绍

Spring Cloud 是一系列框架的集合&#xff0c;它基于 Spring Boot 提供了在分布式系统&#xff08;如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话和集群状态&#xff09;中快速构建一些常见模式的工具。下面是对 Sprin…...

HTML5实现好看的喜庆圣诞节网站源码

HTML5实现好看的喜庆圣诞节网站源码 前言一、设计来源1.1 主界面1.2 圣诞介绍界面1.3 圣诞象征界面1.4 圣诞活动界面1.5 圣诞热度界面1.6 圣诞纪念界面1.7 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现好看的喜庆圣诞节网站源码&#xff0c;圣…...

《学习之道》

《学习之道》主要讲述了以下内容&#xff1a; 学习的原理 大脑的两种认知模式&#xff1a;介绍了专注模式和发散模式。专注模式适合集中精力解决具体问题、进行深度理解和记忆推理&#xff0c;但长时间使用易疲惫和陷入思维定式&#xff1b;发散模式则让大脑在更广泛的认知网…...

【Unity3D】ECS入门学习(十一)ComponentSystem、JobComponentSystem

ComponentSystem&#xff1a;仅支持主线程执行&#xff0c;不支持多线程&#xff0c;并且无法使用SystemBase介绍的扩展方法。 using Unity.Entities; using Unity.Transforms; using Unity.Mathematics;/// <summary> /// 仅主线程执行&#xff0c;不支持多线程 /// &l…...

力扣刷题:栈和队列OJ篇(上)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 目录 1.用队列实现栈&#xff08;1&#xff09;题目…...

XGPT用户帮助手册

文章目录 2024 更新日志2024.12.272024.12.29 摘要 本文详细介绍了XGPT软件的功能及发展历程。XGPT是一款融合了当前最先进人工智能技术的多模态智能软件&#xff0c;专为国内用户优化设计。除了强大的智能问答功能外&#xff0c;XGPT还结合日常办公和科学研究的需求&#xff0…...

Oracle 数据库 dmp文件从高版本导入低版本的问题处理

当前有个需求是将oracle 19c上的数据备份恢复到oracle 11g上使用。我们通过exp命令远程进行备份&#xff0c;然后通过imp进行恢复时出现IMP-00010: not a valid export file, header failed verification报错。 这是数据库版本问题&#xff0c;在使用exp命令导出的时候使用的客…...

ShardingSphere-Proxy分表场景测试案例

快速入门文章参考&#xff1a;《ShardingSphereProxy:快速入门》 基于K8S部署文章参考&#xff1a;《基于K8s部署ShardingSphere-Proxy》 基于golang的测试用例参考&#xff1a;《ShardingSphere-Proxy 连接实战&#xff1a;从 Golang 原生 SQL 到 GORM 的应用》 背景 我们…...

学技术学英文:Tomcat的线程模型调优

导读&#xff1a; tomcat 线程调优关键需要理解下面这几个参数&#xff1a; 1. maxConnections 描述&#xff1a;指定服务器能够同时接受和处理的最大连接数。也就是说&#xff0c;服务器在任何时候都能处理的最大并发连接数。作用&#xff1a;限制服务器在任何给定时间点能…...

创建flutter项目遇到无法连接源的问题

Flutter 环境信息 Flutter版本: 3.19.4 (channel stable) Framework: revision 68bfaea224 (2024-03-20) Engine: revision a5c24f538d Dart: 3.3.2 DevTools: 2.31.1 项目基本信息 项目路径: D:\F\luichun 域名: www.luichun.com.cn 支持平台: android, web, windows 项目创…...

MAC系统QT图标踩坑记录

MAC系统QT图标踩坑记录 1. 准备图标1.1 方法一&#xff1a;下载准备好的图标1.2 方法二&#xff1a;自己生成图标1.2.1 准备一个png文件1.2.2 用sips生成不同大小的图片1.2.3 用iconutil生成图标文件 2. 配置图标2.1. 把图标改命成自己想要的名字&#xff0c;如icon.icns&#…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...