Spring Security 源码分析 --- WebSecurity
概述
spring security 源码分析系列文章。
源码分析
我们想一下,我们使用 ss 框架的步骤是怎么样的。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private MyUserDetailsService myUserDetailsService; @Override
protected void configure(HttpSecurity http) throws Exception {
//spring security 放行注册中心健康检查
http.authorizeRequests()
.antMatchers("/login/**", "/client/exit","/actuator/**","/assets/**").permitAll()
// .anyRequest().authenticated() // 其他地址的访问均需验证权限
// .antMatchers("/hello**").hasAuthority("ROLE_ADMIN")
.anyRequest().authenticated().and()
.logout().deleteCookies("remove").invalidateHttpSession(false)
.and()
.formLogin()
.loginPage("/login").and().csrf().disable().cors();
} @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
} //认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
} @Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
} @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} @Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:8081","http://localhost:9090","http://localhost:8091","http://192.168.0.102:8082","http://localhost:8082"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
configuration.setAllowedHeaders(Arrays.asList("x-requested-with"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
可以看到添加了 @EnableWebSecurity 的注解,同时创建了一个继承 WebSecurityConfigurerAdapter 。 @EnableWebSecurity 我们先来看一下这个注解实现了什么。
@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;
}
可以看到 import 了三个配置文件 。先来看看 WebSecurityConfiguration 类。 通过阅读类的注释我们知道以下消息
* - 使用 WebSecurity 去生成一个 FilterChainProxy
* - export 了几个必要的 beans
* - 自定义可以继承 WebSecurityConfigurerAdapter 来实现
下面我们来看一下 WebSecurityConfiguration 这个类内部执行了什么。
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity; private Boolean debugEnabled; private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers; private ClassLoader beanClassLoader; @Autowired(required = false)
private ObjectPostProcessor<Object> objectObjectPostProcessor; @Bean
public static DelegatingApplicationListener delegatingApplicationListener() {
return new DelegatingApplicationListener();
} @Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
return webSecurity.getExpressionHandler();
} /**
* 创建一个 Spring Security Filter Chain
*
* Creates the Spring Security Filter Chain
* @return
* @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
/**
*
* 存在多个 webSecurityConfigurer 创建一个 WebSecurityConfigurerAdapter 之后走 webSecurity.apply
* 最后执行 webSecurity.build()返回 Filter
*
*/
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
} /**
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP
* tag support.
* @return the {@link WebInvocationPrivilegeEvaluator}
* @throws Exception
*/
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public WebInvocationPrivilegeEvaluator privilegeEvaluator() throws Exception {
return webSecurity.getPrivilegeEvaluator();
} /**
* 使用 SecurityConfigurer<FilterChainProxy, WebSecurityBuilder> 实例来创建一个 webSecurity
*
* Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>}
* instances used to create the web configuration.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} used to create a
* {@link WebSecurity} instance
* @param webSecurityConfigurers the
* {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to
* create the web configuration
* @throws Exception
*/
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//使用 @Value 获取到参数,见下面代码 //创建 webSecurity
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
} //依据@Order 排序 ,并检查排序
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE); //比较类的定义就在下面 Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> 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;
} //最后各个 SecurityConfigurer<Filter, WebSecurity> 都会放到一个调用同一个 webSecurity.apply 方法
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
} //字段赋值
this.webSecurityConfigurers = webSecurityConfigurers;
} @Bean
public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
} /**
* A custom verision of the Spring provided AnnotationAwareOrderComparator that uses
* {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class
* instances for the {@link Order} annotation.
*
* @author Rob Winch
* @since 3.2
*/
private static class AnnotationAwareOrderComparator extends OrderComparator {
private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator(); @Override
protected int getOrder(Object obj) {
return lookupOrder(obj);
} private static int lookupOrder(Object obj) {
if (obj instanceof Ordered) {
return ((Ordered) obj).getOrder();
}
if (obj != null) {
Class<?> clazz = (obj instanceof Class ? (Class<?>) obj : obj.getClass());
Order order = AnnotationUtils.findAnnotation(clazz, Order.class);
if (order != null) {
return order.value();
}
}
return Ordered.LOWEST_PRECEDENCE;
}
} /*
* (non-Javadoc)
*
* @see org.springframework.context.annotation.ImportAware#setImportMetadata(org.
* springframework.core.type.AnnotationMetadata)
*/
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableWebSecurityAttrMap = importMetadata
.getAnnotationAttributes(EnableWebSecurity.class.getName());
AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes
.fromMap(enableWebSecurityAttrMap);
debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
if (webSecurity != null) {
webSecurity.debug(debugEnabled);
}
} /*
* (non-Javadoc)
*
* @see
* org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.
* lang.ClassLoader)
*/
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
} final class AutowiredWebSecurityConfigurersIgnoreParents { private final ConfigurableListableBeanFactory beanFactory; public AutowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "beanFactory cannot be null");
this.beanFactory = beanFactory;
} // 很简单,从一个 beanFactory 查找 webSecurityConfigurer
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
可以看到内部维护这两个重要的字段 :
- List<SecurityConfigurer<Filter, WebSecurity>>
- WebSecurity
再利用他们来生成 Filter . SecurityConfigurer 看名字可以猜出是和配置相关的。 我们先看这个方法
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception { //创建 webSecurity
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
} //集合排序,以 order 大小
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE); Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> 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;
} //调用 webSecurity.apply 方法
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
//填充字段
this.webSecurityConfigurers = webSecurityConfigurers;
}
而这个方法中的形参中的 securityConfigure 是从哪里来的呢? 即是 @value 里的值。
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
同样在WebSecurityConfiguration 类内
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
进去 AutowiredWebSecurityConfigurersIgnoreParents 类看看。
final class AutowiredWebSecurityConfigurersIgnoreParents { private final ConfigurableListableBeanFactory beanFactory; public AutowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "beanFactory cannot be null");
this.beanFactory = beanFactory;
} @SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
可以看到基本的逻辑就是从 beanFactory 中获取 SecurityConfigurer 的子类,这不是就是我们开始讲使用 Spring Security 时继承了一个 WebSecurityConfigurerAdapter 的类(该类可以对Spring Security 进行配置)。
通过上面的代码我们可以知道@EnableWebSecurity 会从环境中获取 SecurityConfigure 安全相关的配置文件,而最终生成一个Filter 类,内部调用的主要的是 WebSecurity 的 apply 和 build 方法。下面我们先深入 WebSecurity 这个类 。
WebSecurity
WebSecurity下文简称 ws, ws 的 build 非常重要而且也非常有意思。先来看看 ws 的类祖宗们。从名字可以大概猜出类的作用。
下面build 方法 ,下面把 AbstractConfigureSecurityBuilder 整个类贴出来,这个类比较重要,注意的她的 doBuild 方法还有apply 方法,顺便提一下 AbstractConfigureSecurityBuilder 同时还是 HttpSecurity 的父类,HttpSecurity 也是个狠角色,会在后面的文章介绍。
public interface SecurityBuilder<O> { /**
* Builds the object and returns it or null.
*
* @return the Object to be built or null if the implementation allows it.
* @throws Exception if an error occurred when building the Object
*/
O build() throws Exception;
}
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean(); private O object; /*
* (non-Javadoc)
*
* @see org.springframework.security.config.annotation.SecurityBuilder#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");
} /**
* Gets the object that was built. If it has not been built yet an Exception is
* thrown.
*
* @return the Object that was built
*/
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
} /**
* Subclasses should implement this to perform the build.
*
* @return the object that should be returned by {@link #build()}.
*
* @throws Exception if an error occurs
*/
protected abstract O doBuild() throws Exception;
}
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.util.Assert;
import org.springframework.web.filter.DelegatingFilterProxy; /**
* <p>
* A base {@link SecurityBuilder} that allows {@link SecurityConfigurer} to be applied to
* it. This makes modifying the {@link SecurityBuilder} a strategy that can be customized
* and broken up into a number of {@link SecurityConfigurer} objects that have more
* specific goals than that of the {@link SecurityBuilder}.
* </p>
*
* <p>
* For example, a {@link SecurityBuilder} may build an {@link DelegatingFilterProxy}, but
* a {@link SecurityConfigurer} might populate the {@link SecurityBuilder} with the
* filters necessary for session management, form based login, authorization, etc.
* </p>
*
* @see WebSecurity
*
* @author Rob Winch
*
* @param <O> The object that this builder returns
* @param <B> The type of this builder (that is returned by the base class)
*/
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
private final Log logger = LogFactory.getLog(getClass()); private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<SecurityConfigurer<O, B>>(); private final Map<Class<? extends Object>, Object> sharedObjects = new HashMap<Class<? extends Object>, Object>(); private final boolean allowConfigurersOfSameType; private BuildState buildState = BuildState.UNBUILT; private ObjectPostProcessor<Object> objectPostProcessor; /***
* Creates a new instance with the provided {@link ObjectPostProcessor}. This post
* processor must support Object since there are many types of objects that may be
* post processed.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
*/
protected AbstractConfiguredSecurityBuilder(
ObjectPostProcessor<Object> objectPostProcessor) {
this(objectPostProcessor, false);
} /***
* Creates a new instance with the provided {@link ObjectPostProcessor}. This post
* processor must support Object since there are many types of objects that may be
* post processed.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
* @param allowConfigurersOfSameType if true, will not override other
* {@link SecurityConfigurer}'s when performing apply
*/
protected AbstractConfiguredSecurityBuilder(
ObjectPostProcessor<Object> objectPostProcessor,
boolean allowConfigurersOfSameType) {
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
} /**
* Similar to {@link #build()} and {@link #getObject()} but checks the state to
* determine if {@link #build()} needs to be called first.
*
* @return the result of {@link #build()} or {@link #getObject()}. If an error occurs
* while building, returns null.
*/
public O getOrBuild() {
if (isUnbuilt()) {
try {
return build();
}
catch (Exception e) {
logger.debug("Failed to perform build. Returning null", e);
return null;
}
}
else {
return getObject();
}
} /**
* Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and
* invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
*
* @param configurer
* @return the {@link SecurityConfigurerAdapter} for further customizations
* @throws Exception
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
throws Exception {
configurer.addObjectPostProcessor(objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
} /**
* 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 extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
} /**
* Sets an object that is shared by multiple {@link SecurityConfigurer}.
*
* @param sharedType the Class to key the shared object by.
* @param object the Object to store
*/
@SuppressWarnings("unchecked")
public <C> void setSharedObject(Class<C> sharedType, C object) {
this.sharedObjects.put(sharedType, object);
} /**
* Gets a shared Object. Note that object heirarchies are not considered.
*
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
@SuppressWarnings("unchecked")
public <C> C getSharedObject(Class<C> sharedType) {
return (C) this.sharedObjects.get(sharedType);
} /**
* Gets the shared objects
* @return the shared Objects
*/
public Map<Class<? extends Object>, Object> getSharedObjects() {
return Collections.unmodifiableMap(this.sharedObjects);
} /**
* Adds {@link SecurityConfigurer} ensuring that it is allowed and invoking
* {@link SecurityConfigurer#init(SecurityBuilder)} immediately if necessary.
*
* @param configurer the {@link SecurityConfigurer} to add
* @throws Exception if an error occurs
*/
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
Assert.notNull(configurer, "configurer cannot be null"); Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<SecurityConfigurer<O, B>>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
} /**
* Gets all the {@link SecurityConfigurer} instances by its class name or an empty
* List if not found. Note that object hierarchies are not considered.
*
* @param clazz the {@link SecurityConfigurer} class to look for
* @return a list of {@link SecurityConfigurer}s for further customization
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) {
List<C> configs = (List<C>) this.configurers.get(clazz);
if (configs == null) {
return new ArrayList<>();
}
return new ArrayList<>(configs);
} /**
* Removes all the {@link SecurityConfigurer} instances by its class name or an empty
* List if not found. Note that object hierarchies are not considered.
*
* @param clazz the {@link SecurityConfigurer} class to look for
* @return a list of {@link SecurityConfigurer}s for further customization
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
List<C> configs = (List<C>) this.configurers.remove(clazz);
if (configs == null) {
return new ArrayList<>();
}
return new ArrayList<>(configs);
} /**
* Gets the {@link SecurityConfigurer} by its class name or <code>null</code> if not
* found. Note that object hierarchies are not considered.
*
* @param clazz
* @return the {@link SecurityConfigurer} for further customizations
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O, B>> C getConfigurer(Class<C> clazz) {
List<SecurityConfigurer<O, B>> configs = this.configurers.get(clazz);
if (configs == null) {
return null;
}
if (configs.size() != 1) {
throw new IllegalStateException("Only one configurer expected for type "
+ clazz + ", but got " + configs);
}
return (C) configs.get(0);
} /**
* Removes and returns the {@link SecurityConfigurer} by its class name or
* <code>null</code> if not found. Note that object hierarchies are not considered.
*
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O, B>> C removeConfigurer(Class<C> clazz) {
List<SecurityConfigurer<O, B>> configs = this.configurers.remove(clazz);
if (configs == null) {
return null;
}
if (configs.size() != 1) {
throw new IllegalStateException("Only one configurer expected for type "
+ clazz + ", but got " + configs);
}
return (C) configs.get(0);
} /**
* Specifies the {@link ObjectPostProcessor} to use.
* @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null
* @return the {@link SecurityBuilder} for further customizations
*/
@SuppressWarnings("unchecked")
public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
return (O) this;
} /**
* Performs post processing of an object. The default is to delegate to the
* {@link ObjectPostProcessor}.
*
* @param object the Object to post process
* @return the possibly modified Object to use
*/
protected <P> P postProcess(P object) {
return this.objectPostProcessor.postProcess(object);
} /**
* Executes the build using the {@link SecurityConfigurer}'s that have been applied
* using the following steps:
*
* <ul>
* <li>Invokes {@link #beforeInit()} for any subclass to hook into</li>
* <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any
* {@link SecurityConfigurer} that was applied to this builder.</li>
* <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li>
* <li>Invokes {@link #performBuild()} which actually builds the Object</li>
* </ul>
*/
@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;
}
} /**
* Invoked prior to invoking each {@link SecurityConfigurer#init(SecurityBuilder)}
* method. Subclasses may override this method to hook into the lifecycle without
* using a {@link SecurityConfigurer}.
*/
protected void beforeInit() throws Exception {
} /**
* Invoked prior to invoking each
* {@link SecurityConfigurer#configure(SecurityBuilder)} method. Subclasses may
* override this method to hook into the lifecycle without using a
* {@link SecurityConfigurer}.
*/
protected void beforeConfigure() throws Exception {
} /**
* Subclasses must implement this method to build the object that is being returned.
*
* @return the Object to be buit or null if the implementation allows it
*/
protected abstract O performBuild() throws Exception; @SuppressWarnings("unchecked")
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
} for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
} @SuppressWarnings("unchecked")
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
} private Collection<SecurityConfigurer<O, B>> getConfigurers() {
List<SecurityConfigurer<O, B>> result = new ArrayList<SecurityConfigurer<O, B>>();
for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
result.addAll(configs);
}
return result;
} /**
* Determines if the object is unbuilt.
* @return true, if unbuilt else false
*/
private boolean isUnbuilt() {
synchronized (configurers) {
return buildState == BuildState.UNBUILT;
}
} /**
* The build state for the application
*
* @author Rob Winch
* @since 3.2
*/
private static enum BuildState {
/**
* This is the state before the {@link Builder#build()} is invoked
*/
UNBUILT(0), /**
* The state from when {@link Builder#build()} is first invoked until all the
* {@link SecurityConfigurer#init(SecurityBuilder)} methods have been invoked.
*/
INITIALIZING(1), /**
* The state from after all {@link SecurityConfigurer#init(SecurityBuilder)} have
* been invoked until after all the
* {@link SecurityConfigurer#configure(SecurityBuilder)} methods have been
* invoked.
*/
CONFIGURING(2), /**
* From the point after all the
* {@link SecurityConfigurer#configure(SecurityBuilder)} have completed to just
* after {@link AbstractConfiguredSecurityBuilder#performBuild()}.
*/
BUILDING(3), /**
* After the object has been completely built.
*/
BUILT(4); private final int order; BuildState(int order) {
this.order = order;
} public boolean isInitializing() {
return INITIALIZING.order == order;
} /**
* Determines if the state is CONFIGURING or later
* @return
*/
public boolean isConfigured() {
return order >= CONFIGURING.order;
}
}
}
通过上面分析,我们知道WebSecurity 执行 build 方法的时候时间会执行到父类的 doBuild 方法
@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;
}
}
其中beforeInit 和 beforeConfigure 这两个方法都是子类继承的,ws 的这两个方法如下 :
@Override
protected void beforeConfigure() throws Exception {
setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
} protected void beforeInit() throws Exception {
}
而父类的 init 方法和 configure 方法
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) {
//调用自身的init 方法
configurer.init((B) this);
} for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
} private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) {
//调用自身的 configure 方法
configurer.configure((B) this);
}
} // this.configurers 是父类持有的一个字段
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
List<SecurityConfigurer<O, B>> result = new ArrayList<SecurityConfigurer<O, B>>();
for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
result.addAll(configs);
}
return result;
}
所以这里就要搞清楚,到底父类的 configurers 字段是什么时候赋值的呢?在apply 方法中,前面我们也见到了 ws 调用了apply方法,我们发现传进 apply 的是 WebSecurityConfigurerAdapter ,即是说将会调用 WebSecurityConfigurerAdapter 的 init 和 configure 方法。 WebSecurityConfigurerAdapter 这个类是不是好熟,这货就是我们在使用Spring Security 时候创建的类啊。
WebSecurityConfigurerAdapter 的 init 和 configure
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);
}
});
} public void configure(WebSecurity web) throws Exception { } 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();
}
继续我们的调用,init 终于出现了我们 WebSecurity 的兄弟---HttpSecurity ,并调用了addSecurityFilterChainBuilder 方法。而 configure 的调用是第二个,即 WebSecurityConfigurerAdapter 没有实现。我们再看一下 ws 的 addSecurityFilterChainBuilder 是什么鬼。
public WebSecurity addSecurityFilterChainBuilder(
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
这里仅仅是为ws中赋值,那么这个值有什么作用呢?在 perferBuild 中会用到,下文会讲解,而HttpSecurity 会在后面的文章介绍,可以看到 init 方法还为 ws 设置了一个拦截器,此处本人还没研究但是我猜测这个拦截器是为了后面 Spring Security 做 mehtod 级别的权限拦截用的,因为文档中提到 method 级别的权限拦截正是用到拦截器来实现的。OK ,那么父类的 init 和 configure 方法就走完了哦,这时候到了 ws 的 performBuild 方法了。
@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<SecurityFilterChain> 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;
}
ignoredRequests 这个可以看到,有多少个ignoredRequests 就会生成多少个 securityFilterChain, 然后遍历 securityFilterChainBuilders 的每一项调用他们的build 的方法,build 方法会返回一个 securityFilterChain ,添加在 ws 的一个字段内,最后通过传入所有的 securityFilterChain 生成一个 FilterChainProxy ,可以知道 FilterChainProxy 必定是一个Filter ,通过名字可以它是一个代理类,这使我们想起了代理的设计模式。 好了,我们是不是漏了什么东西?是的,securityFilterChainBuilders 是什么东西?它是我们之前传入到 ws 的 HttpSecurity .而这里调用调用了securityFilterChainBuilders 的每一项调用他们的build 的方法,实际就是调用了 HttpSecurity 的 build 方法。
下一篇我们将要学习 HttpSecurity 这个类。
参考资料
- https://blog.csdn.net/qq_30905661/article/details/81082453
- https://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle/#tech-intro-access-control (重要,要看)
- https://docs.spring.io/spring-security/site/docs/current/reference/html/authorization.html (认证)
- https://spring.io/guides/topicals/spring-security-architecture
- https://www.jianshu.com/u/fb66b7412d27 (spring security 原理分析过程,非常重要,要看)
Spring Security 源码分析 --- WebSecurity的更多相关文章
- Spring Security 源码分析(四):Spring Social实现微信社交登录
社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...
- spring security源码分析心得
看了半天的文档及源码,终于理出了spring-security的一些总体思路,spring security主要分认证(authentication)和授权(authority). 1.认证authe ...
- spring security源码分析之web包分析
Spring 是一个非常流行和成功的 Java 应用开发框架.Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案.一般来说,Web 应用的安全性包括 ...
- spring security源码分析之core包
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring I ...
- spring security源码分析之一springSecurityFilterChain
1. spring和spring security的集成,配置web.xml如下: <context-param> <param-name>contextConfigLocat ...
- 精尽Spring MVC源码分析 - 寻找遗失的 web.xml
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring Boot源码分析 - Jar 包的启动实现
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- Spring mvc源码分析系列--Servlet的前世今生
Spring mvc源码分析系列--Servlet的前世今生 概述 上一篇文章Spring mvc源码分析系列--前言挖了坑,但是由于最近需求繁忙,一直没有时间填坑.今天暂且来填一个小坑,这篇文章我们 ...
- spring事务源码分析结合mybatis源码(一)
最近想提升,苦逼程序猿,想了想还是拿最熟悉,之前也一直想看但没看的spring源码来看吧,正好最近在弄事务这部分的东西,就看了下,同时写下随笔记录下,以备后查. spring tx源码分析 这里只分析 ...
随机推荐
- h264 RTP STAP-A单时间聚合包
参考官方文档:http://www.rosoo.net/Files/UpFiles/RsProduct/avtools/2009-4/2009491562537854.txt 聚合包的RTP荷载格式的 ...
- linux上安装git以及使用
用git --version命令检查是否已经安装 在CentOS5的版本,由于yum源中没有git,所以需要预先安装一系列的依赖包.在CentOS6的yum源中已经有git的版本了,可以直接使用yum ...
- java基础之 修饰符
一.访问修饰符 Java中,可以使用访问控制符来保护对类.变量.方法和构造方法的访问.Java 支持 4 种不同的访问权限. 1.default (即缺省,什么也不写): 在同一包内可见,不使用任何修 ...
- IntelliJ IDEA 2017.3尚硅谷-----配置 Tomcat
- python网络编程(通过tcp或者udp协议通信)
1.基于tcp协议传送文件: 客户端: import socketimport osimport jsonimport structclient = socket.socket()client.con ...
- util之Map
1.定义 Map<String, Integer> map = new HashMap<String,Integer>(); 2.判断map中是否存在某个键的值: if(map ...
- String类中的equals方法总结(转载)
转载:https://blog.csdn.net/qq_25827845/article/details/53868815 1.String源码中equals大致写法: public boolean ...
- 机器学习基础梳理—(accuracy,precision,recall浅谈)
一.TP TN FP FN TP:标签为正例,预测为正例(P),即预测正确(T) TN:标签为负例,预测为负例(N),即预测正确(T) FP:标签为负例,预测为正例(P),即预测错误(F) FN:标签 ...
- Service Worker,Web Worker,WebSocket的对比
Service Worker 处理网络请求的后台服务.适用于离线和后台同步数据或推送信息.不能直接和dom交互.通过postMessage方法交互. Web Worker 模拟多线程,允许复杂计算功能 ...
- php商城数据库的设计 之无限分类
商品分类,使用无限分类 即: -------如何创建数据表 pid---父级分类id,如果是顶级分类则为0 path---1,用户分类的排序 . 排序示例: 实现逻辑:获取type表的所有分类,ord ...