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. 和旧版本的区别(Security5.7以前的版本)2. Security6.x的默认筛…...

【每日学点鸿蒙知识】无障碍、getLastLocation、蓝牙问题、卡片大小、关系型数据库等
1、是否有类似无障碍辅助相关的API? 场景描述:锁机app,需要通过无障碍能力辅助检测当前正在打开的app,以及模拟用户操作, 关闭用户想要屏蔽的app 可参考:https://developer.huawei.com/consumer/cn/doc/h…...

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

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

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

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

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

Mac 环境 VVenC 编译与编码命令行工具使用教程
VVenC VVenC 是一个开源的高效视频编码器,专门用于支持 H.266/VVC (Versatile Video Coding) 标准的编码。H.266/VVC 是继 HEVC (H.265) 之后的新一代视频编码标准,主要目的是提供比 HEVC 更高的压缩效率,同时保持或提高视频质量。H.266/VVC…...

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

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

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

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

【Unity3D】ECS入门学习(十一)ComponentSystem、JobComponentSystem
ComponentSystem:仅支持主线程执行,不支持多线程,并且无法使用SystemBase介绍的扩展方法。 using Unity.Entities; using Unity.Transforms; using Unity.Mathematics;/// <summary> /// 仅主线程执行,不支持多线程 /// &l…...

力扣刷题:栈和队列OJ篇(上)
大家好,这里是小编的博客频道 小编的博客:就爱学编程 很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!! 目录 1.用队列实现栈(1)题目…...

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

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

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

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

创建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 方法一:下载准备好的图标1.2 方法二:自己生成图标1.2.1 准备一个png文件1.2.2 用sips生成不同大小的图片1.2.3 用iconutil生成图标文件 2. 配置图标2.1. 把图标改命成自己想要的名字,如icon.icns&#…...

TF-IDF(Term Frequency-Inverse Document Frequency)详解:原理和python实现(中英双语)
中文版 TF-IDF算法详解:理解与应用 TF-IDF(Term Frequency-Inverse Document Frequency)是信息检索与文本挖掘中常用的算法,广泛应用于搜索引擎、推荐系统以及各种文本分析领域。TF-IDF的核心思想是通过计算一个词在文档中的重要…...

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

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

客户案例:基于慧集通集成平台,打通屠宰管理系统与用友U8C 系统的全攻略
一、引言 本原型客户成立于2014年,是一家集饲草种植、肉牛养殖、精深加工、冷链物流、餐饮服务于一体的大型农牧综合体。公司下设三个子公司分别涵盖农业、畜牧业、肉制品加工业与餐饮物流服务业。公司严格按照一二三产业融合发展要求,以肉牛产业化为支…...

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

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后使用了一段时间,真是越用越难受,只想说🗑️。。。 找一圈qt creator的快捷键 0. 快捷键界面 这里的搜索真的是…无语,不考虑是人查找吗?? 1. 代码前后浏览 2. 移动代码 3. 半自动导入…...

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

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

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