java security 详解_Spring Security 初始化流程详解

最近在整合微服务OAuth 2认证过程中,它是基于Spring Security之上,而本人对Spring Security架构原理并不太熟悉,导致很多配置搞不太清楚,遂咬牙啃完了Spring Security核心源码,花了差不多一星期,总体上来说,其代码确实比较晦涩,之前在学习Apache Shiro框架之前也曾经在相关论坛里了解过,相比Spring Security,Apache Shiro真的是相当轻量,代码研读起来容易很多,而Spring Security类继承结构复杂,大量使用了其所谓Builder和Configuer模式,其代码跟踪过程很痛苦,遂记录下,分享给有需要的人,由于本人能力有限,在文章中有不对之处,还请各位执教,在此谢谢各位了。

本人研读的Spring Security版本为:5.1.4.RELEASE

Spring Security在3.2版本之后支持Java Configuration,即:通过Java编码形式配置Spring Security,可不再依赖XML文件配置,本文采用Java Configuration方式。

在Spring Security官方文档中有一个最简配置例子:

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.*;

import org.springframework.security.config.annotation.authentication.builders.*;

import org.springframework.security.config.annotation.web.configuration.*;

@Configuration

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

auth

.inMemoryAuthentication()

.withUser("user").password("password").roles("USER");

}

}

我们先不要看其它内容,先关注注解@EnableWebSecurity,它是初始化Spring Security的入口,打开其源码如下:

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)

@Target(value = { java.lang.annotation.ElementType.TYPE })

@Documented

@Import({ WebSecurityConfiguration.class,

SpringWebMvcImportSelector.class,

OAuth2ImportSelector.class })

@EnableGlobalAuthentication

@Configuration

public @interface EnableWebSecurity {

/**

* Controls debugging support for Spring Security. Default is false.

* @return if true, enables debug support with Spring Security

*/

boolean debug() default false;

}

该注解类通过@Configuration和@Import配合使用引入了一个配置类(WebSecurityConfiguration)和两个ImportSelector(SpringWebMvcImportSelector,OAuth2ImportSelector),我们重点关注下WebSecurityConfiguration,它是Spring Security的核心,正是它构建初始化了所有的Bean实例和相关配置,下面我们详细分析下。

打开WebSecurityConfiguration源码,发现它被@Configuration标记,说明它是配置类,

@Configuration

public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware

该类中最重要的工作就是实例并注册FilterChainProxy,也就是我们在以前XML文件中配置的过滤器:

springSecurityFilterChain

org.springframework.web.filter.DelegatingFilterProxy

springSecurityFilterChain

/*

该过滤器负责拦截请求,并把请求通过一定的匹配规则(通过RequestMatcher匹配实现)路由(或者Delegate)到具体的SecurityFilterChain,源码如下:

/**

* Creates the Spring Security Filter Chain

* @return the {@link Filter} that represents the security filter chain

* @throws Exception

*/

@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();

}

@Bean注解name属性值AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME就是XML中定义的springSecurityFilterChain。

从源码中知道过滤器通过最后的webSecurity.build()创建,webSecurity的类型为:WebSecurity,它在setFilterChainProxySecurityConfigurer方法中优先被创建了:

/**

* Sets the {@code }

* instances used to create the web configuration.

*

* @param objectPostProcessor the {@link ObjectPostProcessor} used to create a

* {@link WebSecurity} instance

* @param webSecurityConfigurers the

* {@code } instances used to

* create the web configuration

* @throws Exception

*/

@Autowired(required = false)

public void setFilterChainProxySecurityConfigurer(

ObjectPostProcessor objectPostProcessor,

@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List> webSecurityConfigurers)

throws Exception {

webSecurity = objectPostProcessor

.postProcess(new WebSecurity(objectPostProcessor));

if (debugEnabled != null) {

webSecurity.debug(debugEnabled);

}

Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);

Integer previousOrder = null;

Object previousConfig = null;

for (SecurityConfigurer config : webSecurityConfigurers) {

Integer order = AnnotationAwareOrderComparator.lookupOrder(config);

if (previousOrder != null && previousOrder.equals(order)) {

throw new IllegalStateException(

"@Order on WebSecurityConfigurers must be unique. Order of "

+ order + " was already used on " + previousConfig + ", so it cannot be used on "

+ config + " too.");

}

previousOrder = order;

previousConfig = config;

}

for (SecurityConfigurer webSecurityConfigurer : webSecurityConfigurers) {

webSecurity.apply(webSecurityConfigurer);

}

this.webSecurityConfigurers = webSecurityConfigurers;

}

从代码中可以看到,它是直接被new出来的:

webSecurity = objectPostProcessor

.postProcess(new WebSecurity(objectPostProcessor));

setFilterChainProxySecurityConfigurer方法参数中需要被注入两个对象:objectPostProcessor和webSecurityConfigurers,objectPostProcessor是在ObjectPostProcessorConfiguration配置类中注册的,而webSecurityConfigurers则是使用了@Value注解方式,注解内容为:#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()},通过源码了解,autowiredWebSecurityConfigurersIgnoreParents是在本类中被注册:

@Bean

public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(

ConfigurableListableBeanFactory beanFactory) {

return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);

}

在AutowiredWebSecurityConfigurersIgnoreParents中定义了方法:getWebSecurityConfigurers:

@SuppressWarnings({ "rawtypes", "unchecked" })

public List> getWebSecurityConfigurers() {

List> webSecurityConfigurers = new ArrayList>();

Map beansOfType = beanFactory

.getBeansOfType(WebSecurityConfigurer.class);

for (Entry entry : beansOfType.entrySet()) {

webSecurityConfigurers.add(entry.getValue());

}

return webSecurityConfigurers;

}

它通过BeanFactory获取了类型为WebSecurityConfigurer的Bean实例列表。回到WebSecurityConfiguration类中的setFilterChainProxySecurityConfigurer方法,它把WebSecurityConfigurer列表设置到了WebSecurity中,源码如下:

for (SecurityConfigurer webSecurityConfigurer : webSecurityConfigurers) {

webSecurity.apply(webSecurityConfigurer);

}

通过apply方法,apply方法其实就是webSecurityConfigurer放入webSecurity维护的configurers属性中,configurers是个LinkedHashMap,源码如下:

/**

* Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any

* {@link SecurityConfigurer} of the exact same class. Note that object hierarchies

* are not considered.

*

* @param configurer

* @return the {@link SecurityConfigurerAdapter} for further customizations

* @throws Exception

*/

public > C apply(C configurer) throws Exception {

add(configurer);

return configurer;

}

其中代码add(configurer)就是将这些webSecurityConfigurer添加到webSecurity的configurers属性中。

现在webSecurity的初始化工作已经完成,现在回到springSecurityFilterChain方法中,它首先检查当前是否配置了webSecurityConfigurer,如果没有的会默认设置一个,并且调用上面提到的apply方法,源码如下:

boolean hasConfigurers = webSecurityConfigurers != null

&& !webSecurityConfigurers.isEmpty();

if (!hasConfigurers) {

WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor

.postProcess(new WebSecurityConfigurerAdapter() {

});

webSecurity.apply(adapter);

}

如果已经存在配置了webSecurityConfigurer,则调用webSecurity.build()进行构建。

在进入build方法之前,首先简单介绍下WebSecurity的继承结构,

e6c692b82a53aa58e28105f8bba1f3ab.png

它实现了SecurityBuilder接口,继承自AbstractConfiguredSecurityBuilder,AbstractConfiguredSecurityBuilder继承自AbstractSecurityBuilder,AbstractSecurityBuilder实现了SecurityBuilder,其中AbstractConfiguredSecurityBuilder实现了通过自定义SecurityConfigurer类来配置SecurityBuilder,上面提到的apply(SecurityConfigurer configurer)就是在该类中实现的,它把configurer保存在它维护的LinkedHashMap>, List>> configurers = new LinkedHashMap>, List>>()中。

调用webSecurity.build()后,首先调用的父类AbstractSecurityBuilder中的build方法:

public final O build() throws Exception {

if (this.building.compareAndSet(false, true)) {

this.object = doBuild();

return this.object;

}

throw new AlreadyBuiltException("This object has already been built");

}

然后调用doBuild(),doBuild()在子类中实现,AbstractConfiguredSecurityBuilder实现了该方法:

@Override

protected final O doBuild() throws Exception {

synchronized (configurers) {

buildState = BuildState.INITIALIZING;

beforeInit();

init();

buildState = BuildState.CONFIGURING;

beforeConfigure();

configure();

buildState = BuildState.BUILDING;

O result = performBuild();

buildState = BuildState.BUILT;

return result;

}

}

在这里重点关注init()、configure()和performBuild(),下面逐个分析它们的作用。

init()方法在AbstractConfiguredSecurityBuilder实现:

private void init() throws Exception {

Collection> configurers = getConfigurers();

for (SecurityConfigurer configurer : configurers) {

configurer.init((B) this);

}

for (SecurityConfigurer configurer : configurersAddedInInitializing) {

configurer.init((B) this);

}

}

它的工作是迭代调用所有配置的SecurityConfigrer的init方法,在这里其实是它的子类WebSecurityConfigurer,因为之前获取时指定的类型就是WebSecurityConfigurer,在上文中提到AutowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()中:

Map beansOfType = beanFactory.getBeansOfType(WebSecurityConfigurer.class);

而实现了WebSecurityConfigurer接口的就是WebSecurityConfigurerAdapter,WebSecurityConfigurerAdapter.init()源码如下:

public void init(final WebSecurity web) throws Exception {

final HttpSecurity http = getHttp();

web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {

public void run() {

FilterSecurityInterceptor securityInterceptor = http

.getSharedObject(FilterSecurityInterceptor.class);

web.securityInterceptor(securityInterceptor);

}

});

}

它只要完成两件重要的事情:

初始化HttpSecurity对象;

设置HttpSecurity对象添加至WebSecurity的securityFilterChainBuilders列表中;

初始化HttpSecurity对象在getHttp()方法中实现:

protected final HttpSecurity getHttp() throws Exception {

if (http != null) {

return http;

}

DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor

.postProcess(new DefaultAuthenticationEventPublisher());

localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

AuthenticationManager authenticationManager = authenticationManager();

authenticationBuilder.parentAuthenticationManager(authenticationManager);

authenticationBuilder.authenticationEventPublisher(eventPublisher);

Map, Object> sharedObjects = createSharedObjects();

http = new HttpSecurity(objectPostProcessor, authenticationBuilder,

sharedObjects);

if (!disableDefaults) {

// @formatter:off

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();

// @formatter:on

ClassLoader classLoader = this.context.getClassLoader();

List defaultHttpConfigurers =

SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {

http.apply(configurer);

}

}

configure(http);

return http;

}

从代码中可以了解,HttpSecurity是直接被new出来的,在创建HttpSecurity之前,首先初始化了AuthenticationManagerBuilder对象,这里有段代码很重要就是: AuthenticationManager authenticationManager = authenticationManager();,它创建AuthenticationManager实例,打开authenticationManager()方法:

protected AuthenticationManager authenticationManager() throws Exception {

if (!authenticationManagerInitialized) {

configure(localConfigureAuthenticationBldr);

if (disableLocalConfigureAuthenticationBldr) {

authenticationManager = authenticationConfiguration

.getAuthenticationManager();

}

else {

authenticationManager = localConfigureAuthenticationBldr.build();

}

authenticationManagerInitialized = true;

}

return authenticationManager;

}

在初始化时,它会调用configure(localConfigureAuthenticationBldr);,默认的实现是:

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

this.disableLocalConfigureAuthenticationBldr = true;

}

【1、个性化配置入口之configure(AuthenticationManagerBuilder auth)】

我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置AuthenticationManager。

构建完authenticationManager实例后,将它设置为authenticationBuilder的父认证管理器:

authenticationBuilder.parentAuthenticationManager(authenticationManager);

并将该authenticationBuilder传入HttpSecurity构造器构建HttpSecurity实例。

构建完HttpSecurity实例后,默认情况下会添加默认的拦截其配置:

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();

最后调用configure(http);,这又是一个可个性化的配置入口,它的默认实现是:

protected void configure(HttpSecurity http) throws Exception {

logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

http

.authorizeRequests()

.anyRequest().authenticated()

.and()

.formLogin().and()

.httpBasic();

}

默认的配置是拦截所有的请求需要认证之后才能访问,如果没有认证,会自动生成一个认证表单要求输入用户名和密码。

【2、个性化配置入口之configure(HttpSecurity http)】

我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置HttpSecurity。

OK,目前为止HttpSecurity已经被初始化,接下去需要设置HttpSecurity对象添加至WebSecurity的securityFilterChainBuilders列表中:

web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {

public void run() {

FilterSecurityInterceptor securityInterceptor = http

.getSharedObject(FilterSecurityInterceptor.class);

web.securityInterceptor(securityInterceptor);

}

});

打开HttpSecurity类结构,和WebSecurity一样,它也实现了SecurityBuilder接口,同样继承自AbstractConfiguredSecurityBuilder。

ce3f15ac2f830e96c538863aaba8fdd7.png

当所有的WebSecurityConfigurer的init方法被调用之后,webSecurity.init()工作就结束了。

接下去调用了webSecurity.configure(),该方法同样是在AbstractConfiguredSecurityBuilder中实现的:

private void configure() throws Exception {

Collection> configurers = getConfigurers();

for (SecurityConfigurer configurer : configurers) {

configurer.configure((B) this);

}

}

它的主要工作是迭代调用所有WebSecurityConfigurer的configurer方法,参数是WebSeucrity本身,这又是另外一个重要的个性化入口:

【3、个性化配置入口之configure(WebSecurity web)】

我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置WebSecurity。

自此,三个重要的个性化入口都已经被调用,即在实现WebSecurityConfigurerAdapter经常需要重写的:

1、configure(AuthenticationManagerBuilder auth);

2、configure(WebSecurity web);

3、configure(HttpSecurity http);

回到webSecurity构建过程,接下去重要的的调用:

O result = performBuild();

该方法在WebSecurityConfigurerAdapter中实现,返回的就是过滤器FilterChainProxy,源码如下:

@Override

protected Filter performBuild() throws Exception {

Assert.state(

!securityFilterChainBuilders.isEmpty(),

() -> "At least one SecurityBuilder extends SecurityFilterChain> needs to be specified. "

+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "

+ "More advanced users can invoke "

+ WebSecurity.class.getSimpleName()

+ ".addSecurityFilterChainBuilder directly");

int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();

List securityFilterChains = new ArrayList<>(

chainSize);

for (RequestMatcher ignoredRequest : ignoredRequests) {

securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));

}

for (SecurityBuilder extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {

securityFilterChains.add(securityFilterChainBuilder.build());

}

FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

if (httpFirewall != null) {

filterChainProxy.setFirewall(httpFirewall);

}

filterChainProxy.afterPropertiesSet();

Filter result = filterChainProxy;

if (debugEnabled) {

logger.warn("\n\n"

+ "********************************************************************\n"

+ "********** Security debugging is enabled. *************\n"

+ "********** This may include sensitive information. *************\n"

+ "********** Do not use in a production system! *************\n"

+ "********************************************************************\n\n");

result = new DebugFilter(filterChainProxy);

}

postBuildAction.run();

return result;

}

首先计算出chainSize,也就是ignoredRequests.size() + securityFilterChainBuilders.size();,如果你不配置ignoredRequests,那就是securityFilterChainBuilders.size(),也就是HttpSecurity的个数,其本质上就是你一共配置几个WebSecurityConfigurerAdapter,因为每个WebSecurityConfigurerAdapter对应一个HttpSecurity,而所谓的ignoredRequests就是FilterChainProxy的请求,默认是没有的,如果你需要条跳过某些请求不需要认证或授权,可以如下配置:

@Override

public void configure(WebSecurity web) throws Exception {

web.ignoring().antMatchers("/statics/**");

}

在上面配置中,所有以/statics开头请求都将被FilterChainProxy忽略。

计算完chainSize后,就会创建List securityFilterChains = new ArrayList<>(chainSize);,遍历所有的HttpSecurity,调用HtppSecurity的build()构建其对应的过滤器链SecurityFilterChain实例,并将SecurityFilterChain添加到securityFilterChains列表中:

for (SecurityBuilder extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {

securityFilterChains.add(securityFilterChainBuilder.build());

}

调用HtppSecurity的build()构建其实和调用WebSecurity的build()构建类类似,父类中方法一次被执行,最后执行本身的performBuild()方法,其源码如下:

@Override

protected DefaultSecurityFilterChain performBuild() throws Exception {

Collections.sort(filters, comparator);

return new DefaultSecurityFilterChain(requestMatcher, filters);

}

构建SecurityFilterChain主要是完成RequestMatcher和对应的过滤器列表,我们都知道在Spring Security中,过滤器执行按顺序顺序的,这个排序就是在performBuild()中完成的,也就是:

Collections.sort(filters, comparator);

它通过一个比较器实现了过滤器的排序,这个比较器就是FilterComparator,有兴趣的朋友可以自己去了解详情。

最后返回的是SecurityFilterChain的默认实现DefaultSecurityFilterChain。

构建完所有SecurityFilterChain后,创建最为重要的FilterChainProxy实例,

FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

构造器中传入SecurityFilterChain列表,如果开启了Debug模式,还会被包装成DebugFilter类型,共开发调试使用,默认是关闭的,可以通过过下面方式开启Debug模式:

@Override

public void configure(WebSecurity web) throws Exception {

web.debug(true);

}

至此Spring Security 初始化完成,我们通过继承WebSecurityConfigurerAdapter来代达到个性化配置目的,文中提到了三个重要的个性化入口,并且WebSecurityConfigurerAdapter是可以配置多个的,其对应的接口就是会存在多个SecurityFilterChain实例,但是它们人仍然在同一个FilterChainProxy中,通过RequestMatcher来匹配并传入到对应的SecurityFilterChain中执行请求。

(全文完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值