以github登录为例,首先创建一个Spring Boot工程,版本为2.1.0.RELEASE
,工程结构如下图:
image.png
pom.xml
如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-client</artifactId> <version>5.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
启动类SecurityApplication.java
:
/** * @author iHelin */@RestController@SpringBootApplicationpublic class SecurityApplication { public static void main(String[] args) { SpringApplication.run(SecurityApplication.class, args); } @GetMapping({"/", "/user"}) public Object get() { OAuth2AuthenticationToken authentication = (OAuth2AuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); OAuth2User principal = authentication.getPrincipal(); return principal.getAttributes(); } }
SercurityConfig.java
:
/** * @author iHelin * @date 2018-11-30 15:47 */@EnableWebSecurity(debug = true)public class SercurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests().anyRequest().authenticated().and() .oauth2Login(); } }
配置文件application.properties
server.port=8080logging.level.org.springframework.security=debug logging.level.org.springframework.boot.autoconfigure.security=debug spring.security.oauth2.client.registration.github.client-id=xxxxxx spring.security.oauth2.client.registration.github.client-secret=xxxxxx
从@EnableWebSecurity
开始说起
SercurityConfig是一个配置类,它继承了WebSecurityConfigurerAdapter
,并标明了@EnableWebSecurity(debug = true)
注解,查看这个注解发现,里面又导入(import)了WebSecurityConfiguration.class
这个配置类,如下图:
image.png
WebSecurityConfiguration是一个自动配置类,它的主要作用创建过滤器链(securityFilterChains)并完成安全配置工作,而这一系列过程主要是通过webSecurity完成的。
系统启动时Spring上下文会首先调用它setFilterChainProxySecurityConfigurer
方法进行webSecurity的初始化,这一步通过反射完成(当然,这不是我们的重点)。然后再调用springSecurityFilterChain
进行webSecurity的配置,具体步骤如下:
首先进入springSecurityFilterChain方法
image.png
接着调用org.springframework.security.config.annotation.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"); }
cas操作进入if语句,进入关键的org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#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; } }
里面是一个同步的代码块,不过这也不是重点,核心在init和performBuild方法,注意现在我们的主语还是webSecurity。首先看init方法:
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); } }
这里主要看第一个for循环,里面会进行一些配置的初始化,其中会有一个我们继承的WebSecurityConfigurerAdapter
的代理,其实也就是我们自己定义的安全配置类SercurityConfig
,调用其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); } }); }
看下getHttp:
protected final HttpSecurity getHttp() throws Exception { ... 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 ... } configure(http); return http; }
里面首先进行默认的配置,这里添加了一个Filter:WebAsyncManagerIntegrationFilter
,继续向下执行,会执行configure方法,它是一个模板方法,也就是这里会执行我们配置类里面覆盖的configure方法,这里就完成了httpSecurity的初始化。
以上步骤都只是webSecurity的init操作,也就是创建了许多的配置器,接下来进入webSecurity的performBuild方法使配置生效,具体过程是调用httpSecurity的config方法,里面会调用上面创建的众多配置器的configure方法,其目的是向过滤器链添加各种Filter,最后还会调用performBuild方法对过滤器进行排序,创建DefaultSecurityFilterChain过滤器链,这里以ExceptionHandlingConfigurer为例。
public void configure(H http) throws Exception { AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http); ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter( entryPoint, getRequestCache(http)); AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http); exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler); exceptionTranslationFilter = postProcess(exceptionTranslationFilter); http.addFilter(exceptionTranslationFilter); }
关键看最后的addFilter方法
public HttpSecurity addFilter(Filter filter) { Class<? extends Filter> filterClass = filter.getClass(); if (!comparator.isRegistered(filterClass)) { throw new IllegalArgumentException( "The Filter class " + filterClass.getName() + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead."); } this.filters.add(filter); return this; }
最终向httpSecurity对象的filters中添加filter。
然后再调用httpSecurity的performBuild方法对filters进行排序:
protected DefaultSecurityFilterChain performBuild() throws Exception { Collections.sort(filters, comparator); return new DefaultSecurityFilterChain(requestMatcher, filters); }
最后返回了一个DefaultSecurityFilterChain对象,至此http的配置宣告完成。再回到webSecurity的performBuild方法,它根据httpSecurity返回的securityFilterChain创建了一个securityFilterChains。
protected Filter performBuild() throws Exception { ... for (RequestMatcher ignoredRequest : ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); } FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); ... Filter result = filterChainProxy; ... return result;
作者:iHelin
链接:https://www.jianshu.com/p/6c705b38f2d9