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

【SpringSecurity源码】过滤器链加载流程


theme: smartblue
highlight: a11y-dark

一、前言及准备

1.1 SpringSecurity过滤器链简单介绍

在Spring Security中,过滤器链(Filter Chain)是由多个过滤器(Filter)组成的,这些过滤器按照一定的顺序对进入应用的请求进行处理。每个过滤器可以执行不同的安全操作,如身份验证、授权、安全上下文的建立等。

过滤器是一种典型的AOP思想,我们将通过源码分析这些过滤器如何被加载以及组成过滤器链。

1.2 SpringSecurity配置类

该类主要对SpringSecurity进行一系列配置,后续过滤器链的初始化和加载也是基于这个配置类。当前配置类仅供参考。注意里面两个很重要的方法 #configure(WebSecurity web)以及#configure(WebSecurity web),他们对过滤器链的初始化很重要。

@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {// 自定义登录成功或失败处理器,退出登录处理器@Autowiredprivate MyAuthenticationService myAuthenticationService;// 自定义验证码过滤器@Autowiredprivate ValidateCodeFilter validateCodeFilter;// 自定义权限不足处理@Autowiredprivate MyAccessDeniedHandler myAccessDeniedHandler;// 权限相关Service@Autowiredprivate PermissionService permissionService;@Overridepublic void configure(WebSecurity web) throws Exception {//解决静态资源被拦截的问题web.ignoring().antMatchers("/css/**", "/images/**", "/js/**", "/code/**");}/*** http请求方法** @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {// 加入用户名密码验证过滤器的前面http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);// 查询数据库所有权限列表List<Permission> list = permissionService.list();for (Permission permission : list) {// 添加请求权限http.authorizeRequests().antMatchers(permission.getPermissionUrl()).hasAuthority(permission.getPermissionTag());}// 设置权限不足的信息http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);http.formLogin()// 开启表单认证.loginPage("/toLoginPage")// 自定义登录页面.loginProcessingUrl("/login")//表单提交的路径.usernameParameter("username").passwordParameter("password")//自定义input的name值.successForwardUrl("/")//登录成功之后跳转的路径.successHandler(myAuthenticationService).failureHandler(myAuthenticationService)//登录成功或者失败的处理.and().logout().logoutUrl("/logout").logoutSuccessHandler(myAuthenticationService).and().rememberMe()//开启记住我功能.tokenValiditySeconds(1209600)//token失效时间 默认是2周.rememberMeParameter("remember-me")//自定义表单input值.tokenRepository(getPersistentTokenRepository()).and().authorizeRequests().antMatchers("/toLoginPage").permitAll()//放行登录页面.anyRequest().authenticated();//关闭csrf防护http.csrf().disable();//加载同源域名下iframe页面http.headers().frameOptions().sameOrigin();// 开启跨域支持http.cors().configurationSource(corsConfigurationSource());}} 

1.3 过滤器链加载方法入口

Spring boot启动中会加载spring.factories文件, 在文件中有对应针对Spring Security的过滤器链
的配置信息,所以spring.factories来找过滤器链加载方法入口

image.png

所以我们的过滤器链加载方法入口位于org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain()

二、 源码导读

SpringSecurity的过滤器链创建本质就是读取SecurityConfig配置类,并且通过核心配置方法:#configure(WebSecurity web)以及#configure(WebSecurity web)生成一个个配置对象,最终每个配置对象会转换成一个过滤器对象,这些过滤器对象组成过滤器链。

前面已经提到了,配置类中的 #configure(WebSecurity web)以及#configure(WebSecurity web)非常重要。他们将分别加载到一个HttpSecurity对象和一个名为ignoredRequests的ArrayList之中,
并且过滤器链也是由这两部分转换后组成

2.1 SpringSecurity配置信息的加载

由前面的内容我们已经定位到过滤器链加载的方法入口位于:
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain()

@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {boolean hasConfigurers = webSecurityConfigurers != null&& !webSecurityConfigurers.isEmpty();if (!hasConfigurers) {WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});webSecurity.apply(adapter);}// 加载方法return webSecurity.build();
}

我们从该方法的webSecurity.build()按照下图一路点击,最终会进入到org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild()
方法,该方法是加载配置和过滤器链的主要方法

image.png

org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild()如下

@Override
protected final O doBuild() throws Exception {synchronized (configurers) {buildState = BuildState.INITIALIZING;beforeInit();// 【1】.初始化,主要构建HttpSecurity对象以及内部的配置对象init();buildState = BuildState.CONFIGURING;beforeConfigure();// 【2】ignoredRequests集合的构建configure();buildState = BuildState.BUILDING;// 【3】组装过滤器链O result = performBuild();buildState = BuildState.BUILT;return result;}

可以看到,当前的configurers加载的就是我们自定义的配置类

image.png

2.1.1 构建带有配置信息的HttpSecurity对象

我们从上述的doBuild()方法体中点击init()方法进入其内部

org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#init()

private void init() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();for (SecurityConfigurer<O, B> configurer : configurers) {// 构建HttpSecurity对象configurer.init((B) this);}for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {configurer.init((B) this);}
}

接着点击构建HttpSecurity的对象的方法configurer.init()方法

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#init(),发现真正构建HttpSecurity的方法其实是 getHttp()

public void init(final WebSecurity web) throws Exception {// 真正构建HttpSecurity的方法final HttpSecurity http = getHttp();// 将HttpSecurity对象作为SecurityFilterChainBuilder对象返回web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);web.securityInterceptor(securityInterceptor);});
}

点击进入 getHttp()

protected final HttpSecurity getHttp() throws Exception {/**省略其他**/// 【1】.新建一个HttpSecurity对象http = new HttpSecurity(objectPostProcessor, authenticationBuilder,sharedObjects);if (!disableDefaults) {// 【2】.设置HttpSecurity对象中configurers配置对象以及filter过滤器对象http.csrf().and() .addFilter(new WebAsyncManagerIntegrationFilter()) .exceptionHandling().and().headers().and().sessionManagement().and().securityContext().and().requestCache().and().anonymous().and().servletApi().and().apply(new DefaultLoginPageConfigurer<>()).and().logout();/**省略**/}// 【3】.执行SecurityConfig中的configure(HttpSecurity http)方法configure(http);return http;
}

观察上述代码我们发现,首先通过new HttpSecurity()新建了一个HttpSecurity对象,并且在下方设置HttpSecurity对象中configurers属性以及filter属性。

当执行完下面代码时,即【2】处的代码

        http.csrf().and() //【添加CsrfConfigurer】.addFilter(new WebAsyncManagerIntegrationFilter())  // 【添加 WebAsyncManagerIntegrationFilter】.exceptionHandling().and() // 【添加 ExceptionHandlingConfigurer】.headers().and() // 【添加 HeadersConfigurer】.sessionManagement().and()// 【添加SessionManagementConfigurer】.securityContext().and()// 【添加SecurityContextConfigurer】.requestCache().and()// 【添加RequestCacheConfigurer】.anonymous().and()// 【添加AnonymousConfigurer】.servletApi().and()// 【添加ServletApiConfigurer】.apply(new DefaultLoginPageConfigurer<>()).and()//【添加DefaultLoginPageConfigurer】.logout(); // 【添加LogoutConfigure】r

HttpSecurity对象中configurers属性以及filter如下,此时size分别为 101

image.png
image.png

执行了【2】的代码后会执行【3】的代码:
configure(http);
该方法会调用SecurityConfig配置类文件的configure()方法,由于此时传入的HttpSecurity对象,所以调用的
SecurityConfig配置类文件中的configure(HttpSecurity http),代码如下(注意该部分为自定义的配置文件中方法,需要伙伴自己去编写,根据自己需求进行配置。当前仅供参考!):

@Override
protected void configure(HttpSecurity http) throws Exception {// 加入用户名密码验证过滤器的前  【添加ValidateCodeFilter】http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);// 查询数据库所有权限列表List<Permission> list = permissionService.list();【添加ExpressionUrlAuthorizationConfigurer】for (Permission permission : list) {// 添加请求权限http.authorizeRequests().antMatchers(permission.getPermissionUrl()).hasAuthority(permission.getPermissionTag());}// 设置权限不足的信息 http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);http.formLogin()// 开启表单认证 【添加FormLoginConfigurer】.loginPage("/toLoginPage")// 自定义登录页面.loginProcessingUrl("/login")//表单提交的路径.usernameParameter("username").passwordParameter("password")//自定义input的name值.successForwardUrl("/")//登录成功之后跳转的路径.successHandler(myAuthenticationService).failureHandler(myAuthenticationService)//登录成功或者失败的处理.and().logout().logoutUrl("/logout").logoutSuccessHandler(myAuthenticationService).and().rememberMe()//开启记住我功能 【添加 RememberMeConfigurer】.tokenValiditySeconds(1209600)//token失效时间 默认是2周.rememberMeParameter("remember-me")//自定义表单input值.tokenRepository(getPersistentTokenRepository()).and().authorizeRequests().antMatchers("/toLoginPage").permitAll()//放行登录页面.anyRequest().authenticated();//关闭csrf防护// 【移除CsrfConfigurer】http.csrf().disable();//加载同源域名下iframe页面http.headers().frameOptions().sameOrigin();// 开启跨域支持// 【添加CorsConfigurer】http.cors().configurationSource(corsConfigurationSource());}

执行完该方法后,configurers属性以及filter属性如下,size变为132

image.png
image.png

HttpSecurity对象构建完成!,之后configurers中每个Configurer对象最终都会转换为Filter对象

2.1.2 列表集合 ignoredRequests 的创建

当执行完init()后,HttpSecurity对象构建完成,并且存放在一个叫做securityFilterChainBuilders的集合对象中。

image.png

此时我们回到org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild(),在执行init()方法之后会有一个configure()方法,点击进去可以看到:

private void configure() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();for (SecurityConfigurer<O, B> configurer : configurers) {// 【加载配置类SecurityConfig中的configure(WebSecurity web)方法】configurer.configure((B) this);}
}

注意看

configurer.configure((B) this);

这句代码,此时的this是一个WebSecurity对象
image.png

仔细想想,在配置类文件中,不正是有一个#configure(WebSecurity web)配置方法吗?没错,这句代码正是执行了配置文件中的该方法。

#configure(WebSecurity web)方法体:

image.png

执行完这一行代码之后,ignoredRequests变量已经被赋值,而且变量中的值是和上面配置方法中配置对应的。

image.png

OK,此时SpringSecurity配置信息的加载就已经完成,接下来就是将这些配置信息转换成过滤器并组成过滤器链

2.2 过滤器链的加载

加载完配置文件后,我们的配置信息分别被存放在两部分ignoredRequestssecurityFilterBulders之中,下面再来看看这两部分是如何构建成过滤器链的。

2.2.1 ignoredRequests 转换成过滤器链

image.png
此时我们再次回到此时我们回到org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild()
找到构造过滤器链的方法performBuild()

org.springframework.security.config.annotation.web.builders.WebSecurity#performBuild()

@Override
protected Filter performBuild() throws Exception {/**忽略部分代码**/【1】计算过滤器链长度int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);【2】将ignoredRequests集合中的对象转为过滤器加入到过滤器链中for (RequestMatcher ignoredRequest : ignoredRequests) {securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));}【3】将securityFilterChainBuilder转为过滤器加入到过滤器链中for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {securityFilterChains.add(securityFilterChainBuilder.build());}/**忽略部分代码**/}/**忽略部分代码**/return result;
}

【1】处代码计算过滤器链长度chainSize,Debug中计算结果如下

image.png

【2】处代码将ignoredRequests集合中的对象转为过滤器加入到过滤器链securityFilterChains
中,执行结果如下

image.png

ignoredRequests转换成过滤器链完成

【3】处的代码就是 securityFilterBulders的的处理。下面接着讲。

2.2.2 securityFilterBulders 转换成过滤器链

在上面的org.springframework.security.config.annotation.web.builders.WebSecurity#performBuild() 方法中通过观察看到,处理securityFilterBulders代码就一句话

securityFilterChains.add(securityFilterChainBuilder.build());

我们从securityFilterChainBuilder.build(),按照下面顺序一路点击:

image.png

我们将会定位到
org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#configure()

private void configure() throws Exception {【1】获取配置对象Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();for (SecurityConfigurer<O, B> configurer : configurers) {【2】将配置对象加入到过滤器链configurer.configure((B) this);}
}

在【1】处,我们将得到从securityFilterChainBuilder中得到configurers配置列表

image.png

在【2】处,会循环配置列表对每个配置对象处理并且最终加入到过滤器链中。
比如当前是ExceptionHandlingConfigurer
那么就会执行
org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer#configure()

@Override
public void configure(H http) {AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);// 【1】创建 ExceptionTranslationFilterExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint, getRequestCache(http));AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);exceptionTranslationFilter = postProcess(exceptionTranslationFilter);//【2】 ExceptionTranslationFilter加入到过滤器链http.addFilter(exceptionTranslationFilter);
}

再比如当前是HeaderConfigurer,那么会执行org.springframework.security.config.annotation.web.configurers.HeadersConfigurer#configure()

@Override
public void configure(H http) {【1】创建 HeaderWriterFilterHeaderWriterFilter headersFilter = createHeaderWriterFilter();【2】加入到过滤器链http.addFilter(headersFilter);
}

也就是说,具体是什么类型的Filter,是根据当前Configurer而决定的

当我们执行完所有的configure()方法,再次回到org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild()

可以看到filter已经被加载出来

image.png

但是,过滤器是有一定顺序的,此时加载出来的过滤器并没有处理顺序。
当方法执行到 performBuild()内部时,会进行排序
org.springframework.security.config.annotation.web.builders.HttpSecurity#performBuild()

@Override
protected DefaultSecurityFilterChain performBuild() {【对过滤器进行排序】filters.sort(comparator);return new DefaultSecurityFilterChain(requestMatcher, filters);
}

排序后的过滤器链:

image.png

到这里,拦截器链加载结束!

相关文章:

【SpringSecurity源码】过滤器链加载流程

theme: smartblue highlight: a11y-dark 一、前言及准备 1.1 SpringSecurity过滤器链简单介绍 在Spring Security中&#xff0c;过滤器链&#xff08;Filter Chain&#xff09;是由多个过滤器&#xff08;Filter&#xff09;组成的&#xff0c;这些过滤器按照一定的顺序对进…...

第9章.Keil5-MDK软件简介

目录 0. 《STM32单片机自学教程》专栏 9.1 主界面 9.2 文本格式编辑 9.3 代码提示&语法检测&代码模版 9.4 其他小技巧 9.4.1 TAB 键的妙用 9.4.2 快速定位函数/变量被定义的地方 9.4.3 快速注释与快速消注释 9.4.4 快速打开头文件 9.4.5 查找替换…...

mysql中utf8字符集中文字节长度统计如何统计到2个字节一个汉字

在 MySQL 的 utf8 字符集中&#xff08;也被称为 utf8mb3&#xff09;&#xff0c;中文字符实际上并不是用2个字节来表示的&#xff0c;而是使用3个字节。这是 UTF-8 编码的一个特性&#xff0c;它使用1到4个字节来表示一个字符&#xff0c;具体取决于字符的 Unicode 码点。 对…...

如何实现Linux双网卡同时连接内网和外网的配置?

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …...

ASCLL码表以及字符的相加减

ASCLL码表完整版及解释_acssll码-CSDN博客 #include <getopt.h> #include <stdio.h> #include <stdlib.h>#define MAX_PATH 256 char filename[MAX_PATH 5];int isdigit(int c) {if (c > 0 && c < 9)return 1;return 0; }int main(int argc…...

一键修复所有dll缺失,教大家解决丢失的dll文件

修复所有DLL&#xff08;动态链接库&#xff09;文件缺失的问题通常不可能通过单一的"一键修复"按钮来实现&#xff0c;因为DLL文件缺失可能由各种不同的原因导致&#xff0c;比如应用程序安装不正确、病毒感染、或系统文件损坏等。 使用内置的系统文件检查器&#x…...

wsl2安装rancher并导入和创建k8s集群

环境准备 安装wsl2点击此文]ubuntu20.04安装docker 点击此文,安装完成后docker镜像仓库改成阿里云镜像加速地址.如果不熟请点击此文 docker 安装rancher 启动wsl,根据官方文档以root身份执行 sudo docker run -d --restartunless-stopped -p 80:80 -p 443:443 --privileged …...

内网环境ubuntu设置静态ip、DNS、路由,不影响网络访问

内网环境通常是有线的&#xff0c;通过服务器的ip、mac、dns地址访问网络才生效的&#xff0c;如果ip地址变了&#xff0c;就不能访问网络了。 如果你的ip地址变了&#xff0c;或者要防止ip变更影响网络访问&#xff0c;就要设置 1、依次点击右上角的电源-设置&#xff0c;在打…...

学习前端第三十七天(静态属性静态方法、类检查、错误处理)

一、静态属性和静态方法 1、静态属性静态方法 在属性和方法前加上static&#xff0c;创建属于类自己的属性和方法 class Person {// 加static&#xff0c;属于类自己的static name "xc"; // 类的name属性static height 183; // 类的height属性static age 20;…...

全网最全的基于电机控制的38类simulink仿真全家桶----新手大礼包

整理了基于电机的38种simulink仿真全家桶&#xff0c;包含多种资料&#xff0c;类型齐全十分适合新手学习使用。包括但是不局限于以下&#xff1a; 1、基于多电平逆变器的无刷直流电机驱动simulink仿真 2、基于负载转矩的感应电机速度控制simulink仿真 3、基于滑膜观测器的永…...

Python使用asyncio包实现异步编程

1. 异步编程 异步编程是一种编程范式&#xff0c;用于处理程序中需要等待异步操作完成后才能继续执行的情况。异步编程允许程序在执行耗时的操作时不被阻塞&#xff0c;而是在等待操作完成时继续执行其他任务。这对于处理诸如文件 I/O、网络请求、定时器等需要等待的操作非常有…...

获取文件夹下的vue文件形成组件,require.context

前言&#xff1a;项目中现有一个文件里面包含所有需要用到的组件&#xff0c;如果一个个的去import&#xff0c;则会非常麻烦&#xff0c;现有require.context去实现&#xff0c; 1、require.context var request require.context(‘./module’, true, /.js$/) require.cont…...

2024软件测试必问的常见面试题1000问!

01、您所熟悉的测试用例设计方法都有哪些&#xff1f;请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。 答&#xff1a;有黑盒和白盒两种测试种类&#xff0c;黑盒有等价类划分法&#xff0c;边界分析法&#xff0c;因果图法和错误猜测法。白盒有逻辑覆盖法&…...

C++列表实现

文章目录 一、listView相关内容主要思想实例全部代码 二、QTreeView 一、listView 相关内容 QAbstractItemModel&#xff1a;一个抽象的类&#xff0c;为数据项模型提供抽象的接口&#xff0c;常见的的数据模型列如&#xff1a;QStringListModel,QStandardItemMode,QDirModel…...

论文合集整理推荐2024.5.15

‍2012年论文合集&#xff1a;论文入口 ‍2019年论文合集&#xff1a;论文入口 2022年论文合集&#xff1a;论文入口 2023年论文合集&#xff1a;论文入口 2024年论文合集&#xff1a;论文入口...

JavaScript的跳转传参方式

在JavaScript中&#xff0c;页面跳转并传递参数通常可以通过几种不同的方式来实现。下面是一些常见的方法&#xff1a; 1.URL参数&#xff08;Query String&#xff09; 这是最常见的方式&#xff0c;通过在URL的末尾添加参数来实现。例如&#xff1a; javascriptwindow.loc…...

非阻塞模式下的读写操作

实现文件IO的非阻塞模式的读写操作 fcntl函数 功能&#xff1a; #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); // arg表示可变参数&#xff0c;由cmd决定 fcntl()对打开的文件描述符fd执行下面描述的操作之一。操作由cmd决…...

Google Ads谷歌广告账户被封停怎么办?

跨境出海业务少不了需要做Google Ads推广业务&#xff1b;其中让投手们闻风丧胆的消息就是帐户被暂停。当 Google 检测到任何违反其政策且可能损害用户在线体验的行为时&#xff0c;就会发生这种情况。那么如何在做广告推广的同时&#xff0c;保证账号不被封禁呢&#xff1f;看…...

AI大模型探索之路-训练篇23:ChatGLM3微调实战-基于P-Tuning V2技术的实践指南

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…...

掌握核心概念:Java高级面试难题精解(一)

Java 高级面试问题及答案 问题1: 在Java中&#xff0c;什么是泛型擦除&#xff1f;为什么需要它&#xff1f; 答案&#xff1a; 泛型擦除是Java编译器的一个特性&#xff0c;它在运行时移除泛型类型信息&#xff0c;以确保类型安全。Java的泛型是在J2SE 1.5中引入的&#xff…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...