SpringSecurity简介+启动流程
springSecurity简介
springSecurity是基于过滤器链的登录验证和权限校验框架,分为启动流程和执行流程,和实际应用三部部分,本章节介绍它的启动流程。后面陆续介绍它的执行流程和在生产实际中springSecurity+jwt的应用。
springSecurity的架构图
这是笔者在学习过程中绘画的关于spring-security的类图,springSecurity采用建造者(builder)设计模式,在securityBuild中定义了build方法,通过抽象类AbstractConfiguredSecurityBuilder进行通用实现,从而执行配置类(SecurityConfigurer)中的init方法和configure方法。在启动流程中会做详细介绍。
springSecurity的启动流程
在springSecurity中是通过WebSecurityConfiguration类进行安全相关的配置。该类中重点关注如下代码
@Autowired注解标注在类上在初始化对象的时候会调用该方法一次,并注入objectPostProcessor对象。@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()表示会执行autowiredWebSecurityConfigurersIgnoreParents类中的getWebSecurityConfigurers方法,并将执行结果注入给webSecurityConfigurers参数。在代码3中对该方法做了详细介绍,请在代码二中看此方法说明,也就是将我们的配置类放到List集合中返回。
代码1
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//通过objectPostProcessor生成WebSecurity对象
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
//如果我们定义了多个配置类,可以实现Ordered接口,并重写getOrder对配置类进行排序,一般只定义一个配置类,所以不需要实现Ordered接口
webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
//在定义order排序编号过程中不允许重复,负责会报错
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;
}
/**
*webSecurty是SecurityBuilder类的实现类,apply方法是将配置类放到List集合中,便于执行SecurityBuilder中的build方法,从而
*执行配置类中的init方法和configure方法,进行相关配置。apply方法请参考代码2
*/
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
apply方法实际上执行的是add方法,此时的this变量指向WebSecurity。buildState变量用四种状态:buildState.UNBUILT(初始化状态前),INITIALIZING(初始化状态),BUILDING(构建中状态),BUILT(构建完成状态),会在执行后面的doBuild方法中体现。add方法实际上是将配置类对象放到webSecurity的configurers(linkedHashMap<Class,List>)集合中,换句话说将配置类(实现了SecurityConfigurer接口)放到实现了SecurityBuilder接口中的集合中。
代码2
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
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");
}
//allowConfigurersOfSameType 未初始化默认的状态是false,代表configurers Map中是否可以存在多个相同类型的配置类,
//true代表可以,false不行,如果是false,代表Map中value集合的大小只能为1.
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<>(1);
}
configs.add(configurer);
//放到configurers Map集合中
this.configurers.put(clazz, configs);
//如果对象时初始化状态,则在configurersAddedInInitializing(List<SecurityConfigurer)也进行存放,此时是
//未构建状态,应该configurersAddedInInitializing集合为空
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
getWebSecurityConfigurers方法中获取实现了WebSecurityConfigurer接口的实现类,并添加到List集合列表返回。如果有用过SpringSecurty的同学就会发现,在使用springSecurity中,我们会定义一个安全配置类,继承WebSecurityConfigurerAdapter,并重写configure方法,用来表明对那些资源做拦截,或开启corf等相关配置。
代码3
final class AutowiredWebSecurityConfigurersIgnoreParents {
@SuppressWarnings({
"rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
通过上述代码在webSecurity对象中已经将配置类放入到WebSecurity的List集合中。最后执行webSecurity的build方法。
代码4
@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)