当前位置: 首页 > 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&#…...

TF-IDF(Term Frequency-Inverse Document Frequency)详解:原理和python实现(中英双语)

中文版 TF-IDF算法详解&#xff1a;理解与应用 TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;是信息检索与文本挖掘中常用的算法&#xff0c;广泛应用于搜索引擎、推荐系统以及各种文本分析领域。TF-IDF的核心思想是通过计算一个词在文档中的重要…...

【竞技宝】CS2:HLTV2024职业选手排名TOP15-xantares

北京时间2024年12月30日&#xff0c;HLTV年度选手排名正在持续公布中&#xff0c;今日凌晨正式公布了今年的TOP15选手为EternalFire战队的xantares选手。 选手简介 xantares是一名来自于土耳其的CS职业选手&#xff0c;出生于1995年&#xff0c;今年已经29岁。早在2012年&…...

Spring-kafka快速Demo示例

使用Spring-Kafka快速发送/接受Kafka消息示例代码&#xff0c;项目结构是最基础的SpringBoot结构&#xff0c;提前安装好Kafka&#xff0c;确保Kafka已经正确启动 pom.xml&#xff0c;根据个人情况更换springboot、java版本等 <?xml version"1.0" encoding&qu…...

客户案例:基于慧集通集成平台,打通屠宰管理系统与用友U8C 系统的全攻略

一、引言 本原型客户成立于2014年&#xff0c;是一家集饲草种植、肉牛养殖、精深加工、冷链物流、餐饮服务于一体的大型农牧综合体。公司下设三个子公司分别涵盖农业、畜牧业、肉制品加工业与餐饮物流服务业。公司严格按照一二三产业融合发展要求&#xff0c;以肉牛产业化为支…...

模型 九屏幕分析法

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。九屏幕法&#xff1a;全方位分析问题的系统工具。 1 九屏幕分析法的应用 1.1 新产品研发的市场分析 一家科技公司计划开发一款新型智能手机&#xff0c;为了全面评估市场潜力和风险&#xff0c;他们…...

Qanything 2.0源码解析系列6 PDF解析逻辑

Qanything 2.0源码解析系列6: PDF解析逻辑 type: Post status: Published date: 2024/12/04 summary: 深入剖析Qanything是如何拆解PDF的,核心是pdf转markdown category: 技术分享 原文:www.feifeixu.top 😀 前言: 在前面的文章中探究了图片是怎么进行解析的,这篇文章对…...

MAC系统QT Creator的快捷键

安装好QT Creator后使用了一段时间&#xff0c;真是越用越难受&#xff0c;只想说&#x1f5d1;️。。。 找一圈qt creator的快捷键 0. 快捷键界面 这里的搜索真的是…无语&#xff0c;不考虑是人查找吗&#xff1f;&#xff1f; 1. 代码前后浏览 2. 移动代码 3. 半自动导入…...

【深度学习】多目标融合算法—样本Loss提权

目录 一、引言 二、样本Loss提权 2.1 技术原理 2.2 技术优缺点 三、总结 一、引言 在朴素的深度学习ctr预估模型中&#xff08;如DNN&#xff09;&#xff0c;通常以一个行为为预估目标&#xff0c;比如通过ctr预估点击率。但实际推荐系统业务场景中&#xff0c;更多是多…...

C 实现植物大战僵尸(四)

C 实现植物大战僵尸&#xff08;四&#xff09; C 实现植物大战僵尸&#xff0c;完结撒花&#xff08;还有个音频稍卡顿的性能问题&#xff0c;待有空优化解决&#xff09;。目前基本的功能模块已经搭建好了&#xff0c;感兴趣的友友可自行尝试编写后续游戏内容 因为 C 站不能…...

Tailwind CSS:现代 CSS 框架的优雅之选

Tailwind CSS&#xff1a;现代 CSS 框架的优雅之选 在现代前端开发中&#xff0c;CSS 的灵活性和复杂性让开发者在设计与实现之间寻找平衡。而 Tailwind CSS 的出现&#xff0c;重新定义了 CSS 框架的使用方式。它是一种原子化的 CSS 工具库&#xff0c;提供了丰富的类名以快速…...