本文是从怪诞140819 搬运而来,作者写的很好,于是搬运之。@怪诞140819 如有冒犯,请私信我
spring security有参考的中文翻译文档https://springcloud.cc/spring-security-zhcn.html
在学习spring security的时候,参考的书籍并不是很多,线上的技术博文也是没有系统的全面的介绍,不得不去参考源代码,发费了大量时间和精力,而我试图也通过一系列的文章,能结合自己的学习经历,循序渐进的表达清楚自己所理解到的点。
所有的文章都是对源代码的解读,最后会给出一个小小的demo。
选用的 spring boot版本1.4.3.RELEASE,spring cloud版本Camden.SR4(后面有结合网关zuul做oauth2 认证授权介绍)。版本非spring 最新版本,选用的是平时使用最多版本。
文章是作为学习笔记性质为目的.当然如果文章能帮到有需要的朋友自然是特别开心和幸福的事情。有理解错误和不到位的点也希望各位指出,一起学习一起成长,一直没有都没敢写公开技术文章,第一本人水平有限,第二是不希望因为自己的错误给别人造成误导。
从文档中可知,spring引入我们的项目只需要分为两步
maven引入需要的包,spring boot中引入的spring security版本为4.1.4
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
配置spring security(spring security官方文档给出的例子,后面简称例子1)
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
我们洁简的应用spring security到我们的项目,然后却开启了非常强大的功能,上面的例子,在内存中配置了一个用户名为user,密码为password,并且拥有USER角色的用户。我们还是要探究一下,spring security到底为我们做了一些什么样的工作。
我们的入手点只有两个,@EnableWebSecurity注解和WebSecurityConfigurerAdapter这个适配器类。
注解源代码
@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 {
boolean debug() default false;
此处还是有必要简单翻译一下官方对于类的注释,以便于更加清楚的理解注解的作用
注释中提到两个很重要的点,以及一个配置示例,
从以上可以稍微总结一下我们下一步需要探究的问题,
WebSecurityConfigurerAdapter到底为我们做了什么工作.上面注解的源代码中Import了一个很重要的配置类WebSecurityConfiguration怎样组件我们的过滤器(或者说过滤器链)
spring security加载流程
如果我们忽略掉细节,只看最重要的步骤,大概如此.下面我们对每一个步骤来做相应的源代码解释
WebSecurityConfiguration中的 setFilterChainProxySecurityConfigurer()方法
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
//T1 使用@Value获取到配置信息
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//T2 创建一个webSecurity 对象
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
//T3对configures进行排序
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
//T4对Order进行比较是否有相同的,由于前面进行了排序,只要比较前后有相同的就可以
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;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
//T5将配置信息配置到webSecurity中
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
T1(注释标记处)获取配置信息
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}"
可以看一下autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()
的源代码
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;
}
其实我们根据debug获取到的来看,这个beansOfType 就是我们定义的继承自WebSecurityConfigurerAdapter的类,通过查看父类的定义,我们知道调用build()方法最后返回的必须是一个Filter对象,可以自行参考顶级父类(或接口)WebSecurityConfigurer和SecurityBuilder
WebSecurityConfiguration中的 springSecurityFilterChain()方法为我们创建了一个名字叫做springSecurityFilterChain的Filter。源代码:
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
//这里的意思是我们是否有自定义配置其实就是是否有注入WebSecurityConfigurer的子类,没有的话,我默认的创建一个默认的,但是默认的我们自己不可修改
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
//请参考上面的代码
webSecurity.apply(adapter);
}
//现在为止webSecurity对象的信息已经填充完毕,我们可以构建一个Filter
return webSecurity.build();
}
请查看代码的注释,我们可以知道,到此为止,已经建立了一个Filter对象,而这个Filter将会拦截掉我们的请求,对请求进行过滤拦截,从而起到对资源进行认证保护的作用。然后这个Filter并非我们自己平时定义的Filter这么简单,这个过滤器也只是一个代理的过滤器而已,里面还会有过滤器链,下一篇文章会针对过滤器链进行编写。
还是从最重要的开始
后面会详细讲解到这些信息,包括这些信息在过滤其中起到什么作用,然后最重要的是,我们要先理清楚过滤器的机制,下一篇会详细讲过滤器链