本文介绍: `Spring Security`默认配置是由 `SecurityAutoConfiguration` 、 `UserDetailsServiceAutoConfiguration`、`SecurityFilterAutoConfiguration`这三个自动配置实现的。

一、自动配置

Spring Security 6.x 系列(1)—— 初识Spring Security我们引入spring-boot-starter-security 依赖,就可以实现登录认证,这些都得益于Spring Boot自动配置

spring-boot-autoconfigure模块集成了对Spring Security自动配置

在这里插入图片描述

默认配置是由 SecurityAutoConfigurationUserDetailsServiceAutoConfigurationSecurityFilterAutoConfiguration这三个自动配置实现的。

二、SecurityAutoConfiguration

SecurityAutoConfiguration 主要是导入SpringBootWebSecurityConfiguration配置类:

@AutoConfiguration(
    before = {UserDetailsServiceAutoConfiguration.class}
)
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
@Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
    public SecurityAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean({AuthenticationEventPublisher.class})
    public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
        return new DefaultAuthenticationEventPublisher(publisher);
    }
}

2.1 SpringBootWebSecurityConfiguration

SpringBootWebSecurityConfiguration 配置类中默认添加@EnableWebSecurity注解,启用Spring Security应用安全配置,完成SecurityFilterChain对象实例化,并设置Http相关规则

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
class SpringBootWebSecurityConfiguration {
    SpringBootWebSecurityConfiguration() {
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnMissingBean(
        name = {"springSecurityFilterChain"}
    )
    @ConditionalOnClass({EnableWebSecurity.class})
    @EnableWebSecurity
    static class WebSecurityEnablerConfiguration {
        WebSecurityEnablerConfiguration() {
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnDefaultWebSecurity
    static class SecurityFilterChainConfiguration {
        SecurityFilterChainConfiguration() {
        }

        @Bean
        @Order(2147483642)
        SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
            http.authorizeHttpRequests((requests) -> {
                // 配置所有的Http请求必须认证
                ((AuthorizeHttpRequestsConfigurer.AuthorizedUrl)requests.anyRequest()).authenticated();
            });
            // 开启表单登录认证
            http.formLogin(Customizer.withDefaults());
            // 开启basic认证
            http.httpBasic(Customizer.withDefaults());
            return (SecurityFilterChain)http.build();
        }
    }
}

2.1.1 DefaultSecurityFilterChain 源码分析

SecurityFilterChain一个接口源码如下:

public interface SecurityFilterChain {

	boolean matches(HttpServletRequest request);

	List<Filter> getFilters();

}

DefaultSecurityFilterChainSpring Security中对其的默认实现类,源码比较简单

public final class DefaultSecurityFilterChain implements SecurityFilterChain {

	private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);

	private final RequestMatcher requestMatcher;

	private final List<Filter> filters;

	public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
		this(requestMatcher, Arrays.asList(filters));
	}

	public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
		if (filters.isEmpty()) {
			logger.info(LogMessage.format("Will not secure %s", requestMatcher));
		}
		else {
			logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
		}
		this.requestMatcher = requestMatcher;
		this.filters = new ArrayList<>(filters);
	}

	public RequestMatcher getRequestMatcher() {
		return this.requestMatcher;
	}

	@Override
	public List<Filter> getFilters() {
		return this.filters;
	}

	@Override
	public boolean matches(HttpServletRequest request) {
		return this.requestMatcher.matches(request);
	}

	@Override
	public String toString() {
		return this.getClass().getSimpleName() + " [RequestMatcher=" + this.requestMatcher + ", Filters=" + this.filters
				+ "]";
	}

}

2.1.2 DefaultSecurityFilterChain 实例分析

在上文源码SecurityFilterChain是通过(SecurityFilterChain)http.build()完成实例化,跟踪进入HttpSecurity#performBuild源码

在这里插入图片描述

从上面看得出HttpSecurity一个构建类,它的使命就是构建DefaultSecurityFilterChain对象debug查看DefaultSecurityFilterChain默认匹配所有请求,并默认存在15过滤器

在这里插入图片描述

当前还有一个疑问,就是HttpSecurity是何时进行对象实例化的?请见下文。

2.2 @EnableWebSecurity

@EnableWebSecurity中会导入多个配置类:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableWebSecurity {
    boolean debug() default false;
}

下文重点介绍WebSecurityConfigurationHttpSecurityConfiguration配置类的作用。

2.2.1 WebSecurityConfiguration

2.2.1.1 FilterChainProxy 实例分析

WebSecurityConfiguration中会通过this.webSecurity.build()方法完成Bean名为springSecurityFilterChainFilterChainProxy对象实例化:

在这里插入图片描述

跟踪进入WebSecurity#performBuild源码

	@Override
	protected Filter performBuild() throws Exception {
		Assert.state(!this.securityFilterChainBuilders.isEmpty(),
				() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
						+ "Typically this is done by exposing a SecurityFilterChain bean. "
						+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
						+ ".addSecurityFilterChainBuilder directly");
		int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
		List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
		List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
		for (RequestMatcher ignoredRequest : this.ignoredRequests) {
			WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
					+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
			SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
			securityFilterChains.add(securityFilterChain);
			requestMatcherPrivilegeEvaluatorsEntries
					.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
		}
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
			SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
			securityFilterChains.add(securityFilterChain);
			requestMatcherPrivilegeEvaluatorsEntries
					.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
		}
		if (this.privilegeEvaluator == null) {
			this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
					requestMatcherPrivilegeEvaluatorsEntries);
		}
		FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
		if (this.httpFirewall != null) {
			filterChainProxy.setFirewall(this.httpFirewall);
		}
		if (this.requestRejectedHandler != null) {
			filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
		}
		else if (!this.observationRegistry.isNoop()) {
			CompositeRequestRejectedHandler requestRejectedHandler = new CompositeRequestRejectedHandler(
					new ObservationMarkingRequestRejectedHandler(this.observationRegistry),
					new HttpStatusRequestRejectedHandler());
			filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
		}
		filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
		filterChainProxy.afterPropertiesSet();

		Filter result = filterChainProxy;
		if (this.debugEnabled) {
			this.logger.warn("nn" + "********************************************************************n"
					+ "**********        Security debugging is enabled.       *************n"
					+ "**********    This may include sensitive information.  *************n"
					+ "**********      Do not use in a production system!     *************n"
					+ "********************************************************************nn");
			result = new DebugFilter(filterChainProxy);
		}

		this.postBuildAction.run();
		return result;
	}

发现WebSecurity同样是一个构建类,它的使命就是构建FilterChainProxy对象FilterChainProxy一个代理类,继承GenericFilterBean(即是Servlet Filter又是Spring Bean),它管理了所有注入Spring IoC容器SecurityFilterChainDefaultSecurityFilterChainSecurityFilterChain的默认实现),而在SecurityFilterChain 中又包含多个Spring Security声明Filter

在这里插入图片描述

2.2.2 HttpSecurityConfiguration

HttpSecurityConfiguration#httpSecurity中默认配置了很多Security Filters并完成HttpSecurity对象实例化(多实例)。

此处对上文提出的HttpSecurity是何时进行对象实例化 的疑问完成了解答,也清晰了在2.1.2章节中为什么DefaultSecurityFilterChain对象默认存在15过滤器的原因。

在这里插入图片描述

三、UserDetailsServiceAutoConfiguration

UserDetailsServiceAutoConfiguration 只是通过加载Yml配置文件生成一个默认用户以便开发测试

@AutoConfiguration
@ConditionalOnClass({AuthenticationManager.class})
@ConditionalOnBean({ObjectPostProcessor.class})
@ConditionalOnMissingBean(
    value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, AuthenticationManagerResolver.class},
    type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector", "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository", "org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository"}
)
public class UserDetailsServiceAutoConfiguration {
    private static final String NOOP_PASSWORD_PREFIX = "{noop}";
    private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\{.+}.*$");
    private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);

    public UserDetailsServiceAutoConfiguration() {
    }

    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {
        SecurityProperties.User user = properties.getUser();
        List<String> roles = user.getRoles();
        return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});
    }

    private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
        String password = user.getPassword();
        if (user.isPasswordGenerated()) {
            logger.warn(String.format("%n%nUsing generated security password: %s%n%nThis generated password is for development use only. Your security configuration must be updated before running your application in production.%n", user.getPassword()));
        }

        return encoder == null &amp;&amp; !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;
    }
}

看到这里我们就能明白解 Spring Security 6.1.x 系列(1)—— 初识Spring Security 中通过在配置文件定义用户名密码生效的原因。

四、SecurityFilterAutoConfiguration

在上文中,Bean名为springSecurityFilterChain对象FilterChainProxy类型,在WebSecurityConfiguration被实例化过,代理Spring Security中所有的SecurityFilterChain

因为Servlet 容器Spring IoC容器之间的Filter生命周期并不匹配

SecurityFilterAutoConfiguration自动配置类中,为了让Spring IoC容器管理Filter生命周期FilterChainProxy(也就是springSecurityFilterChain)便交由Spring Web下的DelegatingFilterProxy代理

而且FilterChainProxy不会在任何过滤器Bean调用标准Servlet过滤器生命周期方法FilterChainProxy生命周期方法委托DelegatingFilterProxy执行

DelegatingFilterProxy是作为Servlet容器和Spring IoC容器的生命周期之间桥接的存在。

@AutoConfiguration(
    after = {SecurityAutoConfiguration.class}
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({SecurityProperties.class})
@ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class})
public class SecurityFilterAutoConfiguration {
    private static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";

    public SecurityFilterAutoConfiguration() {
    }

    @Bean
    @ConditionalOnBean(
        name = {"springSecurityFilterChain"}
    )
    public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {
        DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);
        registration.setOrder(securityProperties.getFilter().getOrder());
        registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));
        return registration;
    }

    private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
        return securityProperties.getFilter().getDispatcherTypes() == null ? null : (EnumSet)securityProperties.getFilter().getDispatcherTypes().stream().map((type) -> {
            return DispatcherType.valueOf(type.name());
        }).collect(Collectors.toCollection(() -> {
            return EnumSet.noneOf(DispatcherType.class);
        }));
    }
}

五、总结

结合本篇源码分析对前文
Spring Security 6.x 系列(2)—— 基于过滤器的基础原理(一) 中提到的DelegatingFilterProxyFilterChainProxy
Spring Security 6.x 系列(3)—— 基于过滤器的基础原理(二)中提到的SecurityFilterChainSecurity Filters概念会有更深刻的理解

原文地址:https://blog.csdn.net/ctwy291314/article/details/134155035

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_10081.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注