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中。

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

在这里插入图片描述

该类同时添加 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的继承关系图:

在这里插入图片描述

3.7 使用示例

3.7.1 InMemoryUserDetailsManagerConfigurer 使用

@Configuration
public class BaseAuthenticationConfig {@Autowiredprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("user").password("{noop}password").roles("USER").and().withUser("admin").password("{noop}password").roles("USER", "ADMIN");}
}

3.7.2 JdbcUserDetailsManagerConfigurer 使用

@Configuration
public class BaseAuthenticationConfig {@AutowiredDataSource dataSource;@Autowiredpublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.jdbcAuthentication().dataSource(dataSource);}
}

3.7.3 DaoAuthenticationConfigurer 使用

@Configuration
public class BaseAuthenticationConfig {@AutowiredUserDetailsService userDetailsService;@Autowiredpublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService);}
}

四、AbstractHttpConfigurer

从这个类名就能大概知道是和Http配置有关。

源码注释:
为在 HttpSecurity上运行的 SecurityConfigurer实例添加一个方便的基类。

/*** Adds a convenient base class for {@link SecurityConfigurer} instances that operate on* {@link HttpSecurity}.** @author Rob Winch*/
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {private SecurityContextHolderStrategy securityContextHolderStrategy;/*** Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh* version of the configuration can be applied.* @return the {@link HttpSecurityBuilder} for additional customizations*/@SuppressWarnings("unchecked")public B disable() {getBuilder().removeConfigurer(getClass());return getBuilder();}@SuppressWarnings("unchecked")public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {addObjectPostProcessor(objectPostProcessor);return (T) this;}protected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {if (this.securityContextHolderStrategy != null) {return this.securityContextHolderStrategy;}ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);String[] names = context.getBeanNamesForType(SecurityContextHolderStrategy.class);if (names.length == 1) {this.securityContextHolderStrategy = context.getBean(SecurityContextHolderStrategy.class);}else {this.securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();}return this.securityContextHolderStrategy;}}

AbstractHttpConfigurer是一个抽象类,继承了SecurityConfigurerAdapter,是模版模式,通过源码我们可知:

  • 继承SecurityConfigurerAdapter时限制了父类的构建对象类型为:DefaultSecurityFilterChain

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

  • disable()方法通过移除configurer来实现禁用某配置器。

  • withObjectPostProcessor()方法为配置器手动添加后置处理器。

  • getSecurityContextHolderStrategy()方法获取SecurityContextHolder的使用策略。

我们来看下AbstractHttpConfigurer子类:

在这里插入图片描述
过滤器的配置都在这里面,我们可通过修改配置来实现自定义业务扩展。

下文我们着重分析下AbstractAuthenticationFilterConfigurerFormLoginConfigurer,其他子类实现在后续的业务实战章节我们再逐个进行分析。

4.1 AbstractAuthenticationFilterConfigurer

源码注释:
用于配置 AbstractAuthenticationFilterConfigurer 的基类。 这仅供内部使用。

/*** Base class for configuring {@link AbstractAuthenticationFilterConfigurer}. This is* intended for internal use only.** @param <T> refers to "this" for returning the current configurer* @param <F> refers to the {@link AbstractAuthenticationProcessingFilter} that is being* built* @author Rob Winch* @since 3.2* @see FormLoginConfigurer*/
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>extends AbstractHttpConfigurer<T, B> {private F authFilter;private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;private SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();private AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;private boolean customLoginPage;private String loginPage;private String loginProcessingUrl;private AuthenticationFailureHandler failureHandler;private boolean permitAll;private String failureUrl;/*** Creates a new instance with minimal defaults*/protected AbstractAuthenticationFilterConfigurer() {setLoginPage("/login");}/*** Creates a new instance* @param authenticationFilter the {@link AbstractAuthenticationProcessingFilter} to* use* @param defaultLoginProcessingUrl the default URL to use for* {@link #loginProcessingUrl(String)}*/protected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) {this();this.authFilter = authenticationFilter;if (defaultLoginProcessingUrl != null) {loginProcessingUrl(defaultLoginProcessingUrl);}}/*** Specifies where users will be redirected after authenticating successfully if they* have not visited a secured page prior to authenticating. This is a shortcut for* calling {@link #defaultSuccessUrl(String, boolean)}.* @param defaultSuccessUrl the default success url* @return the {@link FormLoginConfigurer} for additional customization*/public final T defaultSuccessUrl(String defaultSuccessUrl) {return defaultSuccessUrl(defaultSuccessUrl, false);}/*** Specifies where users will be redirected after authenticating successfully if they* have not visited a secured page prior to authenticating or {@code alwaysUse} is* true. This is a shortcut for calling* {@link #successHandler(AuthenticationSuccessHandler)}.* @param defaultSuccessUrl the default success url* @param alwaysUse true if the {@code defaultSuccessUrl} should be used after* authentication despite if a protected page had been previously visited* @return the {@link FormLoginConfigurer} for additional customization*/public final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) {SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();handler.setDefaultTargetUrl(defaultSuccessUrl);handler.setAlwaysUseDefaultTargetUrl(alwaysUse);this.defaultSuccessHandler = handler;return successHandler(handler);}/*** Specifies the URL to validate the credentials.* @param loginProcessingUrl the URL to validate username and password* @return the {@link FormLoginConfigurer} for additional customization*/public T loginProcessingUrl(String loginProcessingUrl) {this.loginProcessingUrl = loginProcessingUrl;this.authFilter.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));return getSelf();}public T securityContextRepository(SecurityContextRepository securityContextRepository) {this.authFilter.setSecurityContextRepository(securityContextRepository);return getSelf();}/*** Create the {@link RequestMatcher} given a loginProcessingUrl* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the* loginProcessingUrl* @return the {@link RequestMatcher} to use based upon the loginProcessingUrl*/protected abstract RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl);/*** Specifies a custom {@link AuthenticationDetailsSource}. The default is* {@link WebAuthenticationDetailsSource}.* @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}* @return the {@link FormLoginConfigurer} for additional customization*/public final T authenticationDetailsSource(AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {this.authenticationDetailsSource = authenticationDetailsSource;return getSelf();}/*** Specifies the {@link AuthenticationSuccessHandler} to be used. The default is* {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties* set.* @param successHandler the {@link AuthenticationSuccessHandler}.* @return the {@link FormLoginConfigurer} for additional customization*/public final T successHandler(AuthenticationSuccessHandler successHandler) {this.successHandler = successHandler;return getSelf();}/*** Equivalent of invoking permitAll(true)* @return the {@link FormLoginConfigurer} for additional customization*/public final T permitAll() {return permitAll(true);}/*** Ensures the urls for {@link #failureUrl(String)} as well as for the* {@link HttpSecurityBuilder}, the {@link #getLoginPage} and* {@link #getLoginProcessingUrl} are granted access to any user.* @param permitAll true to grant access to the URLs false to skip this step* @return the {@link FormLoginConfigurer} for additional customization*/public final T permitAll(boolean permitAll) {this.permitAll = permitAll;return getSelf();}/*** The URL to send users if authentication fails. This is a shortcut for invoking* {@link #failureHandler(AuthenticationFailureHandler)}. The default is* "/login?error".* @param authenticationFailureUrl the URL to send users if authentication fails (i.e.* "/login?error").* @return the {@link FormLoginConfigurer} for additional customization*/public final T failureUrl(String authenticationFailureUrl) {T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl));this.failureUrl = authenticationFailureUrl;return result;}/*** Specifies the {@link AuthenticationFailureHandler} to use when authentication* fails. The default is redirecting to "/login?error" using* {@link SimpleUrlAuthenticationFailureHandler}* @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use* when authentication fails.* @return the {@link FormLoginConfigurer} for additional customization*/public final T failureHandler(AuthenticationFailureHandler authenticationFailureHandler) {this.failureUrl = null;this.failureHandler = authenticationFailureHandler;return getSelf();}@Overridepublic void init(B http) throws Exception {updateAuthenticationDefaults();updateAccessDefaults(http);registerDefaultAuthenticationEntryPoint(http);}@SuppressWarnings("unchecked")protected final void registerDefaultAuthenticationEntryPoint(B http) {registerAuthenticationEntryPoint(http, this.authenticationEntryPoint);}@SuppressWarnings("unchecked")protected final void registerAuthenticationEntryPoint(B http, AuthenticationEntryPoint authenticationEntryPoint) {ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);if (exceptionHandling == null) {return;}exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint),getAuthenticationEntryPointMatcher(http));}protected final RequestMatcher getAuthenticationEntryPointMatcher(B http) {ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);if (contentNegotiationStrategy == null) {contentNegotiationStrategy = new HeaderContentNegotiationStrategy();}MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,MediaType.TEXT_PLAIN);mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));RequestMatcher notXRequestedWith = new NegatedRequestMatcher(new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));}@Overridepublic void configure(B http) throws Exception {PortMapper portMapper = http.getSharedObject(PortMapper.class);if (portMapper != null) {this.authenticationEntryPoint.setPortMapper(portMapper);}RequestCache requestCache = http.getSharedObject(RequestCache.class);if (requestCache != null) {this.defaultSuccessHandler.setRequestCache(requestCache);}this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));this.authFilter.setAuthenticationSuccessHandler(this.successHandler);this.authFilter.setAuthenticationFailureHandler(this.failureHandler);if (this.authenticationDetailsSource != null) {this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);}SessionAuthenticationStrategy sessionAuthenticationStrategy = http.getSharedObject(SessionAuthenticationStrategy.class);if (sessionAuthenticationStrategy != null) {this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);}RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);if (rememberMeServices != null) {this.authFilter.setRememberMeServices(rememberMeServices);}SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {SecurityContextRepository securityContextRepository = securityContextConfigurer.getSecurityContextRepository();this.authFilter.setSecurityContextRepository(securityContextRepository);}this.authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());F filter = postProcess(this.authFilter);http.addFilter(filter);}/*** <p>* Specifies the URL to send users to if login is required. If used with* {@link EnableWebSecurity} a default login page will be generated when this* attribute is not specified.* </p>** <p>* If a URL is specified or this is not being used in conjunction with* {@link EnableWebSecurity}, users are required to process the specified URL to* generate a login page.* </p>*/protected T loginPage(String loginPage) {setLoginPage(loginPage);updateAuthenticationDefaults();this.customLoginPage = true;return getSelf();}/*** @return true if a custom login page has been specified, else false*/public final boolean isCustomLoginPage() {return this.customLoginPage;}/*** Gets the Authentication Filter* @return the Authentication Filter*/protected final F getAuthenticationFilter() {return this.authFilter;}/*** Sets the Authentication Filter* @param authFilter the Authentication Filter*/protected final void setAuthenticationFilter(F authFilter) {this.authFilter = authFilter;}/*** Gets the login page* @return the login page*/protected final String getLoginPage() {return this.loginPage;}/*** Gets the Authentication Entry Point* @return the Authentication Entry Point*/protected final AuthenticationEntryPoint getAuthenticationEntryPoint() {return this.authenticationEntryPoint;}/*** Gets the URL to submit an authentication request to (i.e. where username/password* must be submitted)* @return the URL to submit an authentication request to*/protected final String getLoginProcessingUrl() {return this.loginProcessingUrl;}/*** Gets the URL to send users to if authentication fails* @return the URL to send users if authentication fails (e.g. "/login?error").*/protected final String getFailureUrl() {return this.failureUrl;}/*** Updates the default values for authentication.*/protected final void updateAuthenticationDefaults() {if (this.loginProcessingUrl == null) {loginProcessingUrl(this.loginPage);}if (this.failureHandler == null) {failureUrl(this.loginPage + "?error");}LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(LogoutConfigurer.class);if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {logoutConfigurer.logoutSuccessUrl(this.loginPage + "?logout");}}/*** Updates the default values for access.*/protected final void updateAccessDefaults(B http) {if (this.permitAll) {PermitAllSupport.permitAll(http, this.loginPage, this.loginProcessingUrl, this.failureUrl);}}/*** Sets the loginPage and updates the {@link AuthenticationEntryPoint}.* @param loginPage*/private void setLoginPage(String loginPage) {this.loginPage = loginPage;this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);}@SuppressWarnings("unchecked")private T getSelf() {return (T) this;}}

AbstractAuthenticationFilterConfigurer也是一个抽象类,继承了AbstractHttpConfigurer,是模版模式,通过源码我们可知:

  • 泛型F继承了AbstractAuthenticationProcessingFilter抽象类,而AbstractAuthenticationProcessingFilter
    继承了GenericFilterBean,意味着F是一个过滤器。

AbstractAuthenticationFilterConfigurer作为配置器,我们只需抓住init方法和configure方法这两个核心即可:

查看init方法:

@Override
public void init(B http) throws Exception {updateAuthenticationDefaults();updateAccessDefaults(http);registerDefaultAuthenticationEntryPoint(http);
}

init方法主要干了三件事:

  • updateAuthenticationDefaults方法更新身份认证的默认值(包括:登录页面、失败处理、登出配置)。
  • updateAccessDefaults方法更新默认放行的访问控制。
  • registerDefaultAuthenticationEntryPoint方法注册默认身份认证端点(未认证处理)。

查看configure方法:

@Override
public void configure(B http) throws Exception {PortMapper portMapper = http.getSharedObject(PortMapper.class);if (portMapper != null) {this.authenticationEntryPoint.setPortMapper(portMapper);}RequestCache requestCache = http.getSharedObject(RequestCache.class);if (requestCache != null) {this.defaultSuccessHandler.setRequestCache(requestCache);}this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));this.authFilter.setAuthenticationSuccessHandler(this.successHandler);this.authFilter.setAuthenticationFailureHandler(this.failureHandler);if (this.authenticationDetailsSource != null) {this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);}SessionAuthenticationStrategy sessionAuthenticationStrategy = http.getSharedObject(SessionAuthenticationStrategy.class);if (sessionAuthenticationStrategy != null) {this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);}RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);if (rememberMeServices != null) {this.authFilter.setRememberMeServices(rememberMeServices);}SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {SecurityContextRepository securityContextRepository = securityContextConfigurer.getSecurityContextRepository();this.authFilter.setSecurityContextRepository(securityContextRepository);}this.authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());F filter = postProcess(this.authFilter);http.addFilter(filter);
}

configure方法逻辑就比较简单了:构建各种各样的回调函数设置给 authFilterauthFilter再去 postProcess中走一圈注册到Spring Ioc容器中,最后再把authFilter添加到过滤器链中。

authFilter来自子类调用父类AbstractAuthenticationFilterConfigurer的构造方法进行赋值。

4.2 FormLoginConfigurer

FormLoginConfigurer在继承AbstractAuthenticationFilterConfigurer时限定了父类泛型FUsernamePasswordAuthenticationFilter类型,也就意味着我们这里最终要配置的过滤是 UsernamePasswordAuthenticationFilter

FormLoginConfigurer重写了init方法,配置了一下默认的登录页面。其他的基本上都是从父类来的,未做太多改变。

源码注释:
添加基于表单的身份认证。 所有属性都有合理的默认值,使得所有参数都是可选的。 如果未指定loginPage(String),框架将生成默认登录页面。

public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extendsAbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {/*** Creates a new instance* @see HttpSecurity#formLogin()*/public FormLoginConfigurer() {super(new UsernamePasswordAuthenticationFilter(), null);usernameParameter("username");passwordParameter("password");}/*** <p>* Specifies the URL to send users to if login is required. If used with* {@link EnableWebSecurity} a default login page will be generated when this* attribute is not specified.* </p>** <p>* If a URL is specified or this is not being used in conjunction with* {@link EnableWebSecurity}, users are required to process the specified URL to* generate a login page. In general, the login page should create a form that submits* a request with the following requirements to work with* {@link UsernamePasswordAuthenticationFilter}:* </p>** <ul>* <li>It must be an HTTP POST</li>* <li>It must be submitted to {@link #loginProcessingUrl(String)}</li>* <li>It should include the username as an HTTP parameter by the name of* {@link #usernameParameter(String)}</li>* <li>It should include the password as an HTTP parameter by the name of* {@link #passwordParameter(String)}</li>* </ul>** <h2>Example login.jsp</h2>** Login pages can be rendered with any technology you choose so long as the rules* above are followed. Below is an example login.jsp that can be used as a quick start* when using JSP's or as a baseline to translate into another view technology.** <pre>* <!-- loginProcessingUrl should correspond to FormLoginConfigurer#loginProcessingUrl. Don't forget to perform a POST -->* &lt;c:url value="/login" var="loginProcessingUrl"/&gt;* &lt;form action="${loginProcessingUrl}" method="post"&gt;*    &lt;fieldset&gt;*        &lt;legend&gt;Please Login&lt;/legend&gt;*        &lt;!-- use param.error assuming FormLoginConfigurer#failureUrl contains the query parameter error --&gt;*        &lt;c:if test="${param.error != null}"&gt;*            &lt;div&gt;*                Failed to login.*                &lt;c:if test="${SPRING_SECURITY_LAST_EXCEPTION != null}"&gt;*                  Reason: &lt;c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" /&gt;*                &lt;/c:if&gt;*            &lt;/div&gt;*        &lt;/c:if&gt;*        &lt;!-- the configured LogoutConfigurer#logoutSuccessUrl is /login?logout and contains the query param logout --&gt;*        &lt;c:if test="${param.logout != null}"&gt;*            &lt;div&gt;*                You have been logged out.*            &lt;/div&gt;*        &lt;/c:if&gt;*        &lt;p&gt;*        &lt;label for="username"&gt;Username&lt;/label&gt;*        &lt;input type="text" id="username" name="username"/&gt;*        &lt;/p&gt;*        &lt;p&gt;*        &lt;label for="password"&gt;Password&lt;/label&gt;*        &lt;input type="password" id="password" name="password"/&gt;*        &lt;/p&gt;*        &lt;!-- if using RememberMeConfigurer make sure remember-me matches RememberMeConfigurer#rememberMeParameter --&gt;*        &lt;p&gt;*        &lt;label for="remember-me"&gt;Remember Me?&lt;/label&gt;*        &lt;input type="checkbox" id="remember-me" name="remember-me"/&gt;*        &lt;/p&gt;*        &lt;div&gt;*            &lt;button type="submit" class="btn"&gt;Log in&lt;/button&gt;*        &lt;/div&gt;*    &lt;/fieldset&gt;* &lt;/form&gt;* </pre>** <h2>Impact on other defaults</h2>** Updating this value, also impacts a number of other default values. For example,* the following are the default values when only formLogin() was specified.** <ul>* <li>/login GET - the login form</li>* <li>/login POST - process the credentials and if valid authenticate the user</li>* <li>/login?error GET - redirect here for failed authentication attempts</li>* <li>/login?logout GET - redirect here after successfully logging out</li>* </ul>** If "/authenticate" was passed to this method it update the defaults as shown below:** <ul>* <li>/authenticate GET - the login form</li>* <li>/authenticate POST - process the credentials and if valid authenticate the user* </li>* <li>/authenticate?error GET - redirect here for failed authentication attempts</li>* <li>/authenticate?logout GET - redirect here after successfully logging out</li>* </ul>* @param loginPage the login page to redirect to if authentication is required (i.e.* "/login")* @return the {@link FormLoginConfigurer} for additional customization*/@Overridepublic FormLoginConfigurer<H> loginPage(String loginPage) {return super.loginPage(loginPage);}/*** The HTTP parameter to look for the username when performing authentication. Default* is "username".* @param usernameParameter the HTTP parameter to look for the username when* performing authentication* @return the {@link FormLoginConfigurer} for additional customization*/public FormLoginConfigurer<H> usernameParameter(String usernameParameter) {getAuthenticationFilter().setUsernameParameter(usernameParameter);return this;}/*** The HTTP parameter to look for the password when performing authentication. Default* is "password".* @param passwordParameter the HTTP parameter to look for the password when* performing authentication* @return the {@link FormLoginConfigurer} for additional customization*/public FormLoginConfigurer<H> passwordParameter(String passwordParameter) {getAuthenticationFilter().setPasswordParameter(passwordParameter);return this;}/*** Forward Authentication Failure Handler* @param forwardUrl the target URL in case of failure* @return the {@link FormLoginConfigurer} for additional customization*/public FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));return this;}/*** Forward Authentication Success Handler* @param forwardUrl the target URL in case of success* @return the {@link FormLoginConfigurer} for additional customization*/public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));return this;}@Overridepublic void init(H http) throws Exception {super.init(http);initDefaultLoginFilter(http);}@Overrideprotected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {return new AntPathRequestMatcher(loginProcessingUrl, "POST");}/*** Gets the HTTP parameter that is used to submit the username.* @return the HTTP parameter that is used to submit the username*/private String getUsernameParameter() {return getAuthenticationFilter().getUsernameParameter();}/*** Gets the HTTP parameter that is used to submit the password.* @return the HTTP parameter that is used to submit the password*/private String getPasswordParameter() {return getAuthenticationFilter().getPasswordParameter();}/*** If available, initializes the {@link DefaultLoginPageGeneratingFilter} shared* object.* @param http the {@link HttpSecurityBuilder} to use*/private void initDefaultLoginFilter(H http) {DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageGeneratingFilter.class);if (loginPageGeneratingFilter != null && !isCustomLoginPage()) {loginPageGeneratingFilter.setFormLoginEnabled(true);loginPageGeneratingFilter.setUsernameParameter(getUsernameParameter());loginPageGeneratingFilter.setPasswordParameter(getPasswordParameter());loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());loginPageGeneratingFilter.setFailureUrl(getFailureUrl());loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());}}}

另外我们日常配置的很多东西也是来自这里:

在这里插入图片描述

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

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

相关文章

HarmonyOS应用程序框架——UIAbility实操

UIAbility概述 UIAbility是一种包含用户界面的应用组件&#xff0c;主要用于和用户进行交互。UIAbility也是系统调度的单元&#xff0c;为应用提供窗口在其中绘制界面。 每一个UIAbility实例&#xff0c;都对应于一个最近任务列表中的任务。 一个应用可以有一个UIAbility&…

定时器TIM HAL库+cubeMX(上)

定时器时钟源APB1 36MHz 一.基本定时器 1.基本框图 2.溢出时间计算 3.配置定时器步骤 TIM_HandleTypeDef g_timx_handle;/* 定时器中断初始化函数 */ void btim_timx_int_init(uint16_t arr, uint16_t psc) {g_timx_handle.Instance TIM6;g_timx_handle.Init.Prescaler p…

【C语言(十三)】

自定义类型&#xff1a;结构体 一、结构体类型的声明 1.1、结构体回顾 结构是⼀些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.1.1、结构的声明 例如描述⼀个学生&#xff1a; struct Stu {char name[20];//名字int age;//年龄c…

uniapp点击按钮,防止按钮多次点击多次触发事件【防抖操作】

图片、 一、在根目录下新建common文件并创建common.js文件&#xff0c;输入下面代码 // 防止处理多次点击function noMultipleClicks(methods, info) {// methods是需要点击后需要执行的函数&#xff0c; info是点击需要传的参数let that this;if (that.noClick) {// 第一次点…

【EI会议征稿中】2024年第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)

2024年第四届人工智能、自动化与高性能计算国际会议&#xff08;AIAHPC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Automation and High Performance Computing 2024第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)将于20…

ubuntu下搜索文件的几种方法

一、whereis命令&#xff1a; whereis命令只能用于程序名的搜索&#xff0c;而且只搜索二进制文件(参数-b)、man说明文件(参数-m)和源代码文件(参数-s)。如果省略参数&#xff0c;则返回所有信息。 whereis的命令格式&#xff1a; whereis [-bmsu] [BMS 目录名 -f ] 文…

CSS——标准流、浮动、Flex布局

1、标准流 标准流也叫文档流&#xff0c;指的是标签在页面中默认的排布规则&#xff0c;例如&#xff1a;块元素独占一行&#xff0c;行内元素可以一行显示多个。 2、浮动 作用&#xff1a;让块元素水平排列 属性名&#xff1a;float 属性值&#xff1a; left&#xff1a;…

three.js模拟太阳系

地球的旋转轨迹目前设置为了圆形&#xff0c;效果&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div><div c…

MES安灯管理:优化生产监控的重要工具

一、MES安灯管理的概念 MES安灯管理是一种基于物理安灯和数字化管理的生产异常管理工具。它通过物理安灯和数字化系统的结合&#xff0c;实现对生产异常的实时监控和及时反馈&#xff0c;从而帮助企业快速响应和解决生产异常&#xff0c;提高生产效率和产品质量。 二、MES系统…

scala编码

1、Scala高级语言 Scala简介 Scala是一门类Java的多范式语言&#xff0c;它整合了面向对象编程和函数式编程的最佳特性。具体来讲Scala运行于Java虚拟机&#xff08;JVM)之上&#xff0c;井且兼容现有的Java程序&#xff0c;同样具有跨平台、可移植性好、方便的垃圾回收等特性…

爬虫的分类

爬虫的分类 网络爬虫按照系统结构和实现技术&#xff0c;大致可分为4类&#xff0c;即通用网络爬虫、聚焦网络爬虫、增量网络爬虫和深层次网络爬虫。 1.通用网络爬虫&#xff1a;搜索引擎的爬虫 比如用户在百度搜索引擎上检索对应关键词时&#xff0c;百度将对关键词进行分析…

Nacos-NacosRule 负载均衡—设置集群使本地服务优先访问

userservice: ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则 NacosRule 权重计算方法 目录 一、介绍 二、示例&#xff08;案例截图&#xff09; 三、总结 一、介绍 NacosRule是AlibabaNacos自己实现的一个负载均衡策略&…

宝塔面板快速搭建本地网站结合内网穿透实现远程访问【无需公网IP】

文章目录 前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面 前言 宝塔面板作为简单好用的服务器运维管理面板&#xff0c;它支持Linux/Windows系统&#xff0c;我们可用它来一键配置LAMP/LNMP环境、网站、数据库、FTP等&…

mac 安装nvm以及切换node版本详细步骤

1、nvm介绍&#xff08;node版本管理工具&#xff09; nvm 可以让你通过命令行快速安装和使用不同版本的node 有时候项目太老&#xff0c;node版本太高,执行npm install命令会报错,可以借助nvm切换低版本的node。 2、安装nvm 在终端执行安装命令 curl -o- https://raw.gith…

HttpComponents: 概述

文章目录 1. 概述2. 生态位 1. 概述 早期的Java想要实现HTTP客户端需要借助URL/URLConnection或者自己手动从Socket开始编码&#xff0c;需要处理大量HTTP协议的具体细节&#xff0c;不但繁琐还容易出错。 Apache Commons HttpClient的诞生就是为了解决这个问题&#xff0c;它…

【高效开发工具系列】DataGrip入门

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

使用 Tailwind CSS 完成导航栏效果

使用 Tailwind CSS 完成导航栏效果 本文将向您介绍如何使用 Tailwind CSS 创建一个漂亮的导航栏。通过逐步演示和示例代码&#xff0c;您将学习如何使用 Tailwind CSS 的类来设计和定制导航栏的样式。 准备工作 在开始之前&#xff0c;请确保已经安装了 Tailwind CSS。如果没…

day33-37-SpringBootV12(整合Spring,SpringMVC,Mybatis,日志,api测试等框架)

ssm spring --> applicationContext.xml配置文件 springmvc --> springmvc.xml配置文件 mybatis —> mybatis-config.xml配置文件 —> springboot优化了之前的框架配置,思想是约定大于配置 一、引言 1.1 初始化配置 为了使用SSM框架去开发&#xff0c;准备SSM…

12.4~12.14概率论复习与相应理解(学习、复习、备考概率论,这一篇就够了)

未分配的题目 概率计算&#xff08;一些转换公式与全概率公式&#xff09;与实际概率 &#xff0c;贝叶斯 一些转换公式 相关性质计算 常规&#xff0c;公式的COV与P 复习相关公式 计算出新表达式的均值&#xff0c;方差&#xff0c;再套正态分布的公式 COV的运算性质 如…

科研论文中PPT图片格式选择与转换:EPS、SVG 和 PDF 的比较

当涉及论文中的图片格式时&#xff0c;导师可能要求使用 EPS 格式的图片。EPS&#xff08;Encapsulated PostScript&#xff09;是一种矢量图格式&#xff0c;它以 PostScript 语言描述图像&#xff0c;能够无损地缩放并保持图像清晰度。与像素图像格式&#xff08;如 PNG 和 J…