Spring Security 6.x 系列(10)—— SecurityConfigurer 配置器及其分支实现源码分析(二)

一、前言

在本系列文章:

Spring Security 6.x 系列(4)—— 基于过滤器链的源码分析(一)
中着重分析了Spring SecuritySpring Boot自动配置、 DefaultSecurityFilterChainFilterChainProxy 的构造过程。

Spring Security 6.x 系列(7)—— SecurityBuilder 继承链源码分析
中详细分析了Spring SecurityWebSecurityHttpSecurityAuthenticationManagerBuilder 三个构造器的公共继承链。

Spring Security 6.x 系列(8)—— SecurityConfigurer 配置器及其分支实现源码分析(一)
中分析SecurityConfigurer配置器及其主要分支实现。

Spring Security 6.x 系列(9)—— 基于过滤器链的源码分析(二)
着重分析了@EnableGlobalAuthentication注解的作用、对AuthenticationConfiguration构造AuthenticationManager过程和上文中未介绍的GlobalAuthenticationConfigurerAdapter 配置器的五个分支实现进行了详细的说明。

今天我们就从未被介绍的SecurityConfigurerAdapter配置器的具体分支实现进行展开。

二、SecurityConfigurerAdapter

SecurityConfigurerAdapter在上文中有过详解介绍,它是SecurityConfigurer的基类,它允许子类仅实现它们感兴趣的方法。它还提供了使用 SecurityConfigurer以及完成后获取正在配置的SecurityBuilder(构造器)的访问权限的机制。

SecurityConfigurerAdapter 的实现主要有三大类:

  • UserDetailsAwareConfigurer
  • AbstractHttpConfigurer
  • LdapAuthenticationProviderConfigurer

考虑到 LDAP 现在使用很少,所以重点介绍前两个。

三、UserDetailsAwareConfigurer

这个类名就能大概知道是和用户详细信息配置有关。

再通过继承关系图,看看UserDetailsAwareConfigurer的顶层架构设计:

在这里插入图片描述

UserDetailsAwareConfigurer是一个抽象类,源码比较简单:

/*** Base class that allows access to the {@link UserDetailsService} for using as a default* value with {@link AuthenticationManagerBuilder}.** @param <B> the type of the {@link ProviderManagerBuilder}* @param <U> the type of {@link UserDetailsService}* @author Rob Winch*/
public abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>extends SecurityConfigurerAdapter<AuthenticationManager, B> {/*** Gets the {@link UserDetailsService} or null if it is not available* @return the {@link UserDetailsService} or null if it is not available*/public abstract U getUserDetailsService();}

通过源码我们可知:

  • 泛型U继承了UserDetailsService接口,也就意味着
    getUserDetailsService()方法返回的对象肯定是UserDetailsService接口的实现。

  • 泛型B继承了ProviderManagerBuilder接口,ProviderManagerBuilder构造器的作用是用来构建AuthenticationManager对象,可就意味UserDetailsAwareConfigurer(配置器)用来配置ProviderManagerBuilder构造器。

3.1 AbstractDaoAuthenticationConfigurer

AbstractDaoAuthenticationConfigurer也是一个抽象类,是模版模式:

/*** Allows configuring a {@link DaoAuthenticationProvider}** @param <B> the type of the {@link SecurityBuilder}* @param <C> the type of {@link AbstractDaoAuthenticationConfigurer} this is* @param <U> The type of {@link UserDetailsService} that is being used* @author Rob Winch* @since 3.2*/
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>extends UserDetailsAwareConfigurer<B, U> {// 声明了一个 providerprivate DaoAuthenticationProvider provider = new DaoAuthenticationProvider();// 声明了一个 userDetailsService 的泛型属性private final U userDetailsService;/*** 创建一个实例* @param userDetailsService,userDetailsService的类型可以是UserDetailsService或者UserDetailsPasswordService*/AbstractDaoAuthenticationConfigurer(U userDetailsService) {this.userDetailsService = userDetailsService;this.provider.setUserDetailsService(userDetailsService);if (userDetailsService instanceof UserDetailsPasswordService) {this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);}}/*** Adds an {@link ObjectPostProcessor} for this class.* @param objectPostProcessor* @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations*/@SuppressWarnings("unchecked")public C withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {addObjectPostProcessor(objectPostProcessor);return (C) this;}/*** Allows specifying the {@link PasswordEncoder} to use with the* {@link DaoAuthenticationProvider}. The default is to use plain text.* @param passwordEncoder The {@link PasswordEncoder} to use.* @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations*/@SuppressWarnings("unchecked")public C passwordEncoder(PasswordEncoder passwordEncoder) {this.provider.setPasswordEncoder(passwordEncoder);return (C) this;}public C userDetailsPasswordManager(UserDetailsPasswordService passwordManager) {this.provider.setUserDetailsPasswordService(passwordManager);return (C) this;}@Overridepublic void configure(B builder) throws Exception {this.provider = postProcess(this.provider);// 向builder添加provider(配置构造器阶段)builder.authenticationProvider(this.provider);}/*** Gets the {@link UserDetailsService} that is used with the* {@link DaoAuthenticationProvider}* @return the {@link UserDetailsService} that is used with the* {@link DaoAuthenticationProvider}*/@Overridepublic U getUserDetailsService() {return this.userDetailsService;}}

通过源码我们可知:

  • AbstractDaoAuthenticationConfigurer初始时创建了一个DaoAuthenticationProvider类型的AuthenticationProvider实例。
  • 为使用者提供设置DaoAuthenticationProvider属性UserDetailsService的功能并指定类型为:UserDetailsService/U serDetailsPasswordService
  • 为使用者提供设置DaoAuthenticationProvider属性PasswordEncoder功能;
  • 为使用者提供设置对象后置处处理器的功能。
  • AbstractDaoAuthenticationConfigurer配置构造器对应的初始化阶段方法为空。
  • AbstractDaoAuthenticationConfigurer配置构造器对应的配置阶段方法:
    • DaoAuthenticationProvider执行后置处理
    • DaoAuthenticationProvider添加到构造器中

3.2 DaoAuthenticationConfigurer

DaoAuthenticationConfigurer继承自 AbstractDaoAuthenticationConfigurer,在构造方法中调用AbstractDaoAuthenticationConfigurer的构造方法。

/*** Allows configuring a {@link DaoAuthenticationProvider}** @param <B> The type of {@link ProviderManagerBuilder} this is* @param <U> The type of {@link UserDetailsService} that is being used* @author Rob Winch* @since 3.2*/
public class DaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>extends AbstractDaoAuthenticationConfigurer<B, DaoAuthenticationConfigurer<B, U>, U> {/*** Creates a new instance* @param userDetailsService*/public DaoAuthenticationConfigurer(U userDetailsService) {super(userDetailsService);}}

3.3 UserDetailsServiceConfigurer

UserDetailsServiceConfigurer继承了AbstractDaoAuthenticationConfigurer,并重写了AbstractDaoAuthenticationConfigurer中的configure方法,在configure方法执行之前加入了initUserDetailsService方法,以方便开发时按照自己的方式去初始化 UserDetailsService。这里的initUserDetailsService方法是空的,会交于子类进行具体实现,也是模版模式。

/*** Allows configuring a {@link UserDetailsService} within a* {@link AuthenticationManagerBuilder}.** @param <B> the type of the {@link ProviderManagerBuilder}* @param <C> the {@link UserDetailsServiceConfigurer} (or this)* @param <U> the type of UserDetailsService being used to allow for returning the* concrete UserDetailsService.* @author Rob Winch* @since 3.2*/
public class UserDetailsServiceConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsServiceConfigurer<B, C, U>, U extends UserDetailsService>extends AbstractDaoAuthenticationConfigurer<B, C, U> {/*** Creates a new instance* @param userDetailsService the {@link UserDetailsService} that should be used*/public UserDetailsServiceConfigurer(U userDetailsService) {super(userDetailsService);}@Overridepublic void configure(B builder) throws Exception {initUserDetailsService();super.configure(builder);}/*** Allows subclasses to initialize the {@link UserDetailsService}. For example, it* might add users, initialize schema, etc.*/protected void initUserDetailsService() throws Exception {}}

3.4 UserDetailsManagerConfigurer

UserDetailsManagerConfigurer继承了UserDetailsServiceConfigurer,并实现了 UserDetailsServiceConfigurer中定义的initUserDetailsService空方法,具体的实现逻辑就是将UserDetailsBuilder所构建出来的 UserDetails以及提前准备好的UserDetails中的用户存储到UserDetailsService中。

该类同时添加 withUser方法用来添加用户,同时还增加了一个UserDetailsBuilder用来构建用户,这些逻辑都比较简单,大家可以自行查看。

/*** Base class for populating an* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}* with a {@link UserDetailsManager}.** @param <B> the type of the {@link SecurityBuilder} that is being configured* @param <C> the type of {@link UserDetailsManagerConfigurer}* @author Rob Winch* @since 3.2*/
public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsManagerConfigurer<B, C>>extends UserDetailsServiceConfigurer<B, C, UserDetailsManager> {private final List<UserDetailsBuilder> userBuilders = new ArrayList<>();private final List<UserDetails> users = new ArrayList<>();protected UserDetailsManagerConfigurer(UserDetailsManager userDetailsManager) {super(userDetailsManager);}/*** Populates the users that have been added.* @throws Exception*/@Overrideprotected void initUserDetailsService() throws Exception {for (UserDetailsBuilder userBuilder : this.userBuilders) {getUserDetailsService().createUser(userBuilder.build());}for (UserDetails userDetails : this.users) {getUserDetailsService().createUser(userDetails);}}/*** Allows adding a user to the {@link UserDetailsManager} that is being created. This* method can be invoked multiple times to add multiple users.* @param userDetails the user to add. Cannot be null.* @return the {@link UserDetailsBuilder} for further customizations*/@SuppressWarnings("unchecked")public final C withUser(UserDetails userDetails) {this.users.add(userDetails);return (C) this;}/*** Allows adding a user to the {@link UserDetailsManager} that is being created. This* method can be invoked multiple times to add multiple users.* @param userBuilder the user to add. Cannot be null.* @return the {@link UserDetailsBuilder} for further customizations*/@SuppressWarnings("unchecked")public final C withUser(User.UserBuilder userBuilder) {this.users.add(userBuilder.build());return (C) this;}/*** Allows adding a user to the {@link UserDetailsManager} that is being created. This* method can be invoked multiple times to add multiple users.* @param username the username for the user being added. Cannot be null.* @return the {@link UserDetailsBuilder} for further customizations*/@SuppressWarnings("unchecked")public final UserDetailsBuilder withUser(String username) {UserDetailsBuilder userBuilder = new UserDetailsBuilder((C) this);userBuilder.username(username);this.userBuilders.add(userBuilder);return userBuilder;}/*** Builds the user to be added. At minimum the username, password, and authorities* should provided. The remaining attributes have reasonable defaults.*/public final class UserDetailsBuilder {private UserBuilder user;private final C builder;/*** Creates a new instance* @param builder the builder to return*/private UserDetailsBuilder(C builder) {this.builder = builder;}/*** Returns the {@link UserDetailsManagerConfigurer} for method chaining (i.e. to* add another user)* @return the {@link UserDetailsManagerConfigurer} for method chaining*/public C and() {return this.builder;}/*** Populates the username. This attribute is required.* @param username the username. Cannot be null.* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)*/private UserDetailsBuilder username(String username) {this.user = User.withUsername(username);return this;}/*** Populates the password. This attribute is required.* @param password the password. Cannot be null.* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)*/public UserDetailsBuilder password(String password) {this.user.password(password);return this;}/*** Populates the roles. This method is a shortcut for calling* {@link #authorities(String...)}, but automatically prefixes each entry with* "ROLE_". This means the following:** <code>*     builder.roles("USER","ADMIN");* </code>** is equivalent to** <code>*     builder.authorities("ROLE_USER","ROLE_ADMIN");* </code>** <p>* This attribute is required, but can also be populated with* {@link #authorities(String...)}.* </p>* @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null,* contain null values or start with "ROLE_"* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)*/public UserDetailsBuilder roles(String... roles) {this.user.roles(roles);return this;}/*** Populates the authorities. This attribute is required.* @param authorities the authorities for this user. Cannot be null, or contain* null values* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)* @see #roles(String...)*/public UserDetailsBuilder authorities(GrantedAuthority... authorities) {this.user.authorities(authorities);return this;}/*** Populates the authorities. This attribute is required.* @param authorities the authorities for this user. Cannot be null, or contain* null values* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)* @see #roles(String...)*/public UserDetailsBuilder authorities(List<? extends GrantedAuthority> authorities) {this.user.authorities(authorities);return this;}/*** Populates the authorities. This attribute is required.* @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN,* etc). Cannot be null, or contain null values* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)* @see #roles(String...)*/public UserDetailsBuilder authorities(String... authorities) {this.user.authorities(authorities);return this;}/*** Defines if the account is expired or not. Default is false.* @param accountExpired true if the account is expired, false otherwise* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)*/public UserDetailsBuilder accountExpired(boolean accountExpired) {this.user.accountExpired(accountExpired);return this;}/*** Defines if the account is locked or not. Default is false.* @param accountLocked true if the account is locked, false otherwise* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)*/public UserDetailsBuilder accountLocked(boolean accountLocked) {this.user.accountLocked(accountLocked);return this;}/*** Defines if the credentials are expired or not. Default is false.* @param credentialsExpired true if the credentials are expired, false otherwise* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)*/public UserDetailsBuilder credentialsExpired(boolean credentialsExpired) {this.user.credentialsExpired(credentialsExpired);return this;}/*** Defines if the account is disabled or not. Default is false.* @param disabled true if the account is disabled, false otherwise* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate* additional attributes for this user)*/public UserDetailsBuilder disabled(boolean disabled) {this.user.disabled(disabled);return this;}UserDetails build() {return this.user.build();}}}

3.5 JdbcUserDetailsManagerConfigurer

JdbcUserDetailsManagerConfigurer继承了UserDetailsManagerConfigurer,在父类的基础上补充了 DataSource对象,同时还提供了相应的数据库查询方法。

/*** Configures an* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}* to have JDBC authentication. It also allows easily adding users to the database used* for authentication and setting up the schema.** <p>* The only required method is the {@link #dataSource(javax.sql.DataSource)} all other* methods have reasonable defaults.** @param <B> the type of the {@link ProviderManagerBuilder} that is being configured* @author Rob Winch* @since 3.2*/
public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>extends UserDetailsManagerConfigurer<B, JdbcUserDetailsManagerConfigurer<B>> {private DataSource dataSource;private List<Resource> initScripts = new ArrayList<>();public JdbcUserDetailsManagerConfigurer(JdbcUserDetailsManager manager) {super(manager);}public JdbcUserDetailsManagerConfigurer() {this(new JdbcUserDetailsManager());}/*** Populates the {@link DataSource} to be used. This is the only required attribute.* @param dataSource the {@link DataSource} to be used. Cannot be null.* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional* customizations*/public JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource) {this.dataSource = dataSource;getUserDetailsService().setDataSource(dataSource);return this;}/*** Sets the query to be used for finding a user by their username. For example:** <code>*     select username,password,enabled from users where username = ?* </code>* @param query The query to use for selecting the username, password, and if the user* is enabled by username. Must contain a single parameter for the username.* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional* customizations*/public JdbcUserDetailsManagerConfigurer<B> usersByUsernameQuery(String query) {getUserDetailsService().setUsersByUsernameQuery(query);return this;}/*** Sets the query to be used for finding a user's authorities by their username. For* example:** <code>*     select username,authority from authorities where username = ?* </code>* @param query The query to use for selecting the username, authority by username.* Must contain a single parameter for the username.* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional* customizations*/public JdbcUserDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query) {getUserDetailsService().setAuthoritiesByUsernameQuery(query);return this;}/*** An SQL statement to query user's group authorities given a username. For example:** <code>*     select*         g.id, g.group_name, ga.authority*     from*         groups g, group_members gm, group_authorities ga*     where*         gm.username = ? and g.id = ga.group_id and g.id = gm.group_id* </code>* @param query The query to use for selecting the authorities by group. Must contain* a single parameter for the username.* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional* customizations*/public JdbcUserDetailsManagerConfigurer<B> groupAuthoritiesByUsername(String query) {JdbcUserDetailsManager userDetailsService = getUserDetailsService();userDetailsService.setEnableGroups(true);userDetailsService.setGroupAuthoritiesByUsernameQuery(query);return this;}/*** A non-empty string prefix that will be added to role strings loaded from persistent* storage (default is "").* @param rolePrefix* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional* customizations*/public JdbcUserDetailsManagerConfigurer<B> rolePrefix(String rolePrefix) {getUserDetailsService().setRolePrefix(rolePrefix);return this;}/*** Defines the {@link UserCache} to use* @param userCache the {@link UserCache} to use* @return the {@link JdbcUserDetailsManagerConfigurer} for further customizations*/public JdbcUserDetailsManagerConfigurer<B> userCache(UserCache userCache) {getUserDetailsService().setUserCache(userCache);return this;}@Overrideprotected void initUserDetailsService() throws Exception {if (!this.initScripts.isEmpty()) {getDataSourceInit().afterPropertiesSet();}super.initUserDetailsService();}@Overridepublic JdbcUserDetailsManager getUserDetailsService() {return (JdbcUserDetailsManager) super.getUserDetailsService();}/*** Populates the default schema that allows users and authorities to be stored.* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional* customizations*/public JdbcUserDetailsManagerConfigurer<B> withDefaultSchema() {this.initScripts.add(new ClassPathResource("org/springframework/security/core/userdetails/jdbc/users.ddl"));return this;}protected DatabasePopulator getDatabasePopulator() {ResourceDatabasePopulator dbp = new ResourceDatabasePopulator();dbp.setScripts(this.initScripts.toArray(new Resource[0]));return dbp;}private DataSourceInitializer getDataSourceInit() {DataSourceInitializer dsi = new DataSourceInitializer();dsi.setDatabasePopulator(getDatabasePopulator());dsi.setDataSource(this.dataSource);return dsi;}}

在实例构造上进一步限制了父类中的U userDetailsService的类型为JdbcUserDetailsManager

JdbcUserDetailsManager的继承关系图:

在这里插入图片描述

3.6 InMemoryUserDetailsManagerConfigurer

InMemoryUserDetailsManagerConfigurer继承了UserDetailsManagerConfigurer,在实例构造上进一步限制了父类中的U userDetailsService的类型为InMemoryUserDetailsManager

/*** Configures an* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}* to have in memory authentication. It also allows easily adding users to the in memory* authentication.** @param <B> the type of the {@link ProviderManagerBuilder} that is being configured* @author Rob Winch* @since 3.2*/
public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>extends UserDetailsManagerConfigurer<B, InMemoryUserDetailsManagerConfigurer<B>> {/*** Creates a new instance*/public InMemoryUserDetailsManagerConfigurer() {super(new InMemoryUserDetailsManager(new ArrayList<>()));}}

InMemoryUserDetailsManager的继承关系图:

在这里插入图片描述

未完待续~~~~!!!!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

思伟老友记 | 晋江市尚亿建材实业有限公司携手思伟软件16年

晋江市尚亿建材实业有限公司 晋江市尚亿建材实业有限公司成立于2006年&#xff0c;建有两个混凝土搅拌站&#xff0c;是晋江市成立时间最长的搅拌站之一。目前拥有25部搅拌车&#xff0c;5部泵送车&#xff0c;3部装载机&#xff0c;混凝土年产量超过50万m。 思伟软件与尚亿公…

数字化升级,智慧医疗新时代——医院陪诊服务的技术创新

在信息技术飞速发展的今天&#xff0c;医疗服务正迎来数字化升级的新时代。本文将探讨如何通过先进技术的应用&#xff0c;为医院陪诊服务注入更多智慧元素&#xff0c;提升患者和家属的医疗体验。 1. 创新医疗预约系统 # Python代码演示医疗预约系统的简单实现 class Medic…

如何在Android平板上远程连接Ubuntu服务器使用code-server代码开发

目录 1.ubuntu本地安装code-server 2. 安装cpolar内网穿透 3. 创建隧道映射本地端口 4. 安卓平板测试访问 5.固定域名公网地址 6.结语 1.ubuntu本地安装code-server 准备一台虚拟机,Ubuntu或者centos都可以&#xff0c;这里以VMwhere ubuntu系统为例 下载code server服务…

汽车电子之深力科推荐安森美车规级芯片

车规级芯片 在汽车制造业&#xff0c;就可靠性要求而言&#xff0c;车规级芯片无疑是要高于商业级和工业级。 而车规级芯片&#xff1a;顾名思义&#xff0c;就是应用到汽车中的芯片&#xff0c;它不同于日常生活中的消费品和工业品&#xff0c;该类芯片对可靠性要求较高&…

C# 语法笔记

1.ref、out&#xff1a;参数传递的两种方式 ref&#xff1a;引用传递 using System; namespace CalculatorApplication {class NumberManipulator{public void swap(ref int x, ref int y){int temp;temp x; /* 保存 x 的值 */x y; /* 把 y 赋值给 x */y temp; /* 把 t…

数据中心:保障企业运营安全可靠的关键

随着人工智能与云计算的爆发&#xff0c;数据中心行业迎来了前所未有的需求增长。然而&#xff0c;这也带来了一系列的挑战。各地政府机构对数据中心建设出台了更为完善和严格的地方标准&#xff0c;企业面临着运营成本高、人才短缺和节能减排等困难。同时&#xff0c;过去频频…

【网络编程】-- 02 端口、通信协议

网络编程 3 端口 端口表示计算机上的一个程序的进程 不同的进程有不同的端口号&#xff01;用来区分不同的软件进程 被规定总共0~65535 TCP,UDP&#xff1a;65535 * 2 在同一协议下&#xff0c;端口号不可以冲突占用 端口分类&#xff1a; 公有端口&#xff1a;0~1023 HT…

2024年AI视频识别技术的6大发展趋势预测

随着人工智能技术的快速发展&#xff0c;AI视频识别技术也将会得到进一步的发展和应用。2023年已经进入尾声&#xff0c;2024年即将来临&#xff0c;那么AI视频识别技术又将迎来怎样的发展趋势&#xff1f;本文将对2023年的AI视频技术做一个简单的盘点并对2024年的发展趋势进行…

【AI】VIT Transformer论文学习笔记

论文&#xff1a;Dosovitskiy A, Beyer L, Kolesnikov A, et al. An image is worth 16x16 words: Transformers for image recognition at scale[J]. arXiv preprint arXiv:2010.11929, 2020 1.文章背景 计算机视觉当前最热门的两大基础模型就是Transformer和CNN了。 Transf…

前段时间的失败总结复盘

分享失败经验&#xff0c;前段时间的总结复盘&#xff1a; 与伙伴合作面对异常决策要及时提出质疑&#xff0c;怼&#xff0c;别太客气&#xff0c;客气起来&#xff0c;小心翼翼在意他人情绪那么这个项目就会让人难受&#xff0c;不要因为因为伙伴身上有标签/光环/权威就觉得…

zookeeper集群+kaafka集群

kafka3.0之前依赖于zookeeper zookeeper开源&#xff0c;分布式的架构&#xff0c;提供协调服务&#xff08;Apache项目&#xff09; 基于观察者模式涉及的分布式服务管理架构 存储和管理数据&#xff0c;分布式节点上的服务接受观察者的注册&#xff0c;一旦分布式节点上的…

《使用ThinkPHP6开发项目》 - 安装ThinkPHP框架

1.安装ThinkPHP6框架 这里我们使用的是composer安装的安装方式&#xff0c;请确保电脑已经安装了composer&#xff0c;如未安装可查看Composer 安装与使用 | 菜鸟教程 composer create-project topthink/think tp 上面命令安装的是稳定版的&#xff0c;也是最新的稳定版&…