SpringSecurity流程补充
SpringSecurity 流程补充
注意:
- 基于 spring-boot-dependencies:2.7.7
- 首先需要了解 springboot2.7 升级
Changes to Auto-configuration
以后使用autoconfigure
进行自动注入 - 代码地址 io.github.poo0054
启动
我们每次添加 <artifactId>spring-boot-starter-security</artifactId>
,启动的时候会有一条类似的日志:
Using generated springSecurity password: 1db8eb87-e2ee-4c72-88e7-9b85268c4430
This generated password is for development use only. Your springSecurity configuration must be updated before running your
application in production.
找到 UserDetailsServiceAutoConfiguration#InMemoryUserDetailsManager
类,它是 springboot 自动装配的。
下面这些都是 springboot 自动装配类,在 spring-boot-autoconfigure-2.7.7.jar
> META-INF > spring > org.springframework.boot.autoconfigure.AutoConfiguration.imports 中。这些类就是 Spring Security 的全部了。
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration
..........
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration
SecurityAutoConfiguration
|
|
@EnableConfigurationProperties(SecurityProperties.class)
这个是 springSecurity 的核心配置类SecurityProperties
,里面能配置
filter
: 过滤,user
: 用户信息
@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })
这里导入了 2 个类 SpringBootWebSecurityConfiguration
和SecurityDataConfiguration
,SecurityDataConfiguration
是 Spring
springSecurity 与 Spring 数据的集成,暂时不做讲解,重点是SpringBootWebSecurityConfiguration
SpringBootWebSecurityConfiguration
这个类就是一个 Configuration
类,条件必须为 @ConditionalOnWebApplication(type = Type.SERVLET)
才会注入
SecurityFilterChainConfiguration
其中第一个子类SecurityFilterChainConfiguration
添加了@ConditionalOnDefaultWebSecurity
,这个类有个注解
@Conditional(DefaultWebSecurityCondition.class)
,而DefaultWebSecurityCondition
类继承了AllNestedConditions
所以下面代码就是判断该类是否生效,如果不存在SecurityFilterChain
和WebSecurityConfigurerAdapter
的 bean,就生效。创建默认的SecurityFilterChain
|
|
SecurityFilterChain
就是整个 springsecurity 的流程了,有俩个方法,一个是boolean matches(HttpServletRequest request);
,是否匹配这次请求,匹配成功就获取当前所有Filter
进行处理
SecurityFilterChain
类会放在最下面单独讲解
ErrorPageSecurityFilterConfiguration
这是第二个子类,主要就是通过FilterRegistrationBean
注入了一个ErrorPageSecurityFilter
。 用于拦截错误调度,以确保对错误页面的授权访问。
|
|
WebSecurityEnablerConfiguration
这个类主要就是添加了@EnableWebSecurity
注解,这个注解也很重要,后面跟SecurityFilterChain
一起讲解
DefaultAuthenticationEventPublisher
在类中还存在SecurityAutoConfiguration
bean,这个是属于 spring 的发布订阅。改装一下,就是 springSecurity 的成功和失败事件,可以订阅失败后的一些处理,如日志打印等
|
|
UserDetailsServiceAutoConfiguration
注入条件
|
|
InMemoryUserDetailsManager
UserDetailsManager 的非持久化实现,支持内存映射。 主要用于测试和演示目的,其中不需要完整的持久系统
SecurityFilterAutoConfiguration
SpringSecurity 的过滤器
自动配置。与 SpringBootWebSecurityConfiguration 分开配置,以确保在存在用户提供的 WebSecurityConfiguration 时,过滤器的顺序仍然被配置。
DelegatingFilterProxyRegistrationBean
这个类是继承了AbstractFilterRegistrationBean
,FilterRegistrationBean
也是继承的AbstractFilterRegistrationBean
。但是DelegatingFilterProxyRegistrationBean
是使用的一个targetBeanName
找到 bean。进行注入。其中private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
也就是springSecurityFilterChain
其中有俩个属性, Order 和 DispatcherTypes
-
Order 默认为-100
-
DispatcherType
就是DispatcherType
类private Set<DispatcherType> dispatcherTypes = new HashSet<>( Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
注意: 这里需要了解一下DelegatingFilterProxyRegistrationBean
以及 spring 如何整合 filter 和 mvc 的。 springSecurity 核心就是 filter
image.png
DelegatingFilterProxyRegistrationBean
和FilterRegistrationBean
都是继承的RegistrationBean
,而RegistrationBean
又是ServletContextInitializer
的实现类。其中void onStartup(ServletContext servletContext)
方法是关键。 在javax.servlet
中,存在这样一个类
|
|
springboot 中的TomcatStarter
继承了这个类,而这个类又是 spring 启动的核心AbstractApplicationContext#refresh()
中的onRefresh();
方法。
找到实现类ServletWebServerApplicationContext
的onRefresh()
方法。里面有createWebServer();
方法。
在里面有创建webServer
的方法。this.webServer = factory.getWebServer(getSelfInitializer());
这个就是创建tomcat
的工厂。
TomcatServletWebServerFactory#getWebServer(ServletContextInitializer... initializers)
。里面就是创建 tomcat,并启动TomcatWebServer#initialize()
(这就是 springboot 不用 tamcat 的原因)
而把 filter 注入 servlet 中的是TomcatServletWebServerFactory#prepareContext(Host host, ServletContextInitializer[] initializers)
中的TomcatServletWebServerFactory#configureContext(context, initializersToUse)
方法,在里面创建了一个TomcatStarter starter = new TomcatStarter(initializers);
。而TomcatStarter
继承了ServletContainerInitializer
类。调用ServletContainerInitializer#onStartup(ServletContext servletContext)
时候会进入到RegistrationBean
中。
然后AbstractFilterRegistrationBean#addRegistration
里面添加 filter
return servletContext.addFilter(getOrDeduceName(filter), filter);
这样每次请求 servlet,tomcat 就会先使用 filter 过滤器进行拦截
简单来说就是TomcatStarter
继承了ServletContainerInitializer
。tomcat 会调用onStartup
方法,在这个方法里面会调用ServletContextInitializer#onStartup
。在这个里面有filter
和其余需要整合ServletContext
的方法
比如springSecurityFilterChain
使用的是DelegatingFilterProxyRegistrationBean
,需要使用 bean 去获取getFilter
。而ErrorPageFilter
使用的是FilterRegistrationBean
。直接就可以注入
@EnableWebSecurity
这个就是 springSecurity 的核心注解
需要注意,@EnableWebMvcSecurity
已经弃用,请使用@EnableWebSecurity
|
|
WebSecurityConfiguration
首先先看类注释(以后都默认翻译成简体中文.ali 翻译):
使用 WebSecurity 创建执行 Spring 安全 web 安全的 FilterChainProxy。然后导出必要的 bean。 可以通过实现 WebSecurityConfigurer WebSecurityConfigurer 并将其公开为 Configuration 或公开 WebSecurityCustomizer bean 来进行自定义。 使用 EnableWebSecurity 时会导入该配置。
setFilterChainProxySecurityConfigurer
逐行解释:
this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
ObjectPostProcessor
也就是AutowireBeanFactoryObjectPostProcessor
。在AuthenticationConfiguration
类上@Import(ObjectPostProcessorConfiguration.class)
.
AutowireBeanFactoryObjectPostProcessor
类里面创建webSecurity
AutowireBeanFactoryObjectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
使用AutowireCapableBeanFactory
创建出WebSecurity
AutowireBeanFactoryObjectPostProcessor
把SmartInitializingSingleton
和DisposableBean
拿出来,使用自己的destroy()
和afterSingletonsInstantiated()
执行
List<SecurityConfigurer<Filter, WebSecurity» webSecurityConfigurers = new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory).getWebSecurityConfigurers();
AutowiredWebSecurityConfigurersIgnoreParents
也就是获取所有的WebSecurityConfigurerAdapter
这里有几个类需要了解SecurityConfigurer
和SecurityBuilder
先了解一下结构
使用WebSecurity
聚合了private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
也就是WebSecurityConfigurerAdapter
(当然还有别的,这里主要讲WebSecurityConfigurerAdapter
)
WebSecurityConfigurerAdapter
也可以认为就是SecurityConfigurer
WebSecurity
也就是SecurityBuilder
然后在SecurityBuilder
的实现类AbstractConfiguredSecurityBuilder
的doBuild()
方法进行很多别的操作。
|
|
回到原来地方,返回的webSecurityConfigurers
,里面的
|
|
然后就到了AbstractConfiguredSecurityBuilder#apply
方法,里面调用了add(configurer);
也就是把SecurityConfigurer
放入了AbstractConfiguredSecurityBuilder#configurers
的一个 map 中,这样就使用SecurityBuilder
聚合了SecurityConfigurer
。
在构建的时候可以做一些事情
也就是说使用WebSecurity
聚合了SecurityConfigurer
(包括WebSecurityConfigurerAdapter
)
this.securityFilterChains = securityFilterChains;
获取所有的securityFilterChains
this.webSecurityCustomizers = webSecurityCustomizers;
获取所有webSecurityCustomizers
public Filter springSecurityFilterChain()
这个里面最关键的也就是这个了。
|
|
首先使用根据获取到的securityFilterChains
set 入WebSecurity#securityFilterChainBuilders
的 List 属性
这里有个需要注意的地方,如果你继承了WebSecurityConfigurerAdapter
。this.securityFilterChains
就是一个空的。
而且会由WebSecurityConfigurerAdapter#getHttp()
进行创建WebSecurity
。这就跟 spring 的 order 有关了。 @Order(SecurityProperties.BASIC_AUTH_ORDER)
其中SpringBootWebSecurityConfiguration#SecurityFilterChainConfiguration
有一个注解@ConditionalOnDefaultWebSecurity
|
|
这里会创建SecurityFilterChain
。 还会有一个HttpSecurity
的注入
继续回到上面,
|
|
FilterSecurityInterceptor
也在这里进行处理,也就是SecurityMetadataSource
元数据
然后自定义的WebSecurityCustomizer
也在这里。可以自行改变webSecurity
|
|
接下来就是构建了,来到 AbstractConfiguredSecurityBuilder#doBuild()
|
|
init(); 如果继承了
WebSecurityConfigurerAdapter
,就会在这里创建HttpSecurity
注意: 这里个buildState
,用来控制当前状态的
beforeConfigure();
在当前是没有什么处理
configure();
这行代码就是我们每次继承WebSecurityConfigurerAdapter
的处理了
O result = performBuild();
然后就到了WebSecurity#performBuild()
。
- 首先排除忽略的
RequestMatcher
- 添加入
securityFilterChain
和requestMatcherPrivilegeEvaluatorsEntries
- 创建出
FilterChainProxy
bean 的名称为springSecurityFilterChain
(重点)
剩下的都是一些创建一些 bean 了。
SecurityExpressionHandler
: 默认为DefaultWebSecurityExpressionHandler
类(Facade 将 springSecurity 评估安全表达式的要求与基础表达式对象的实现隔离)
WebInvocationPrivilegeEvaluator
: 为 WebSecurity#performBuild()
中创建的 requestMatcherPrivilegeEvaluatorsEntries
使用RequestMatcherDelegatingWebInvocationPrivilegeEvaluator
包装。(允许用户确定他们是否具有给定 web URI 的特权。)
这俩个类都是很重要的。 一个是解析器,一个是判断 uri 是否合格的类。 后面单独讲
HttpSecurityConfiguration
接下来到了HttpSecurityConfiguration
根据上面WebSecurityConfiguration
,可以得出。WebSecurityConfiguration
创建WebSecurity
,WebSecurity
创建了FilterChainProxy
的 bean。
HttpSecurityConfiguration
创建HttpSecurity
。 而在SecurityFilterChainConfiguration
类中,使用HttpSecurity
创建了SecurityFilterChain
。这也就是我们使用了WebSecurityConfigurerAdapter
。为什么会存在SecurityFilterChain
类的原因。是SecurityFilterChainConfiguration#defaultSecurityFilterChain
创建了一个SecurityFilterChain
。
得出结论,FilterChainProxy
持有SecurityFilterChain
。而DelegatingFilterProxyRegistrationBean
又持有FilterChainProxy
DelegatingFilterProxyRegistrationBean
->FilterChainProxy
->SecurityFilterChain
其实到了这一步。后面的就是我们自己编写的代码了比如
|
|
或者继承 WebSecurityConfigurerAdapter 的类了。
这里我们用 springSecurity 默认的SpringBootWebSecurityConfiguration
来举例
|
|
首先获取HttpSecurity http
(这里的HttpSecurity
是从HttpSecurityConfiguration
里面创建的,如果是自定义的SecurityFilterChain
就是从自己写的里面来的)
我们来到
HttpSecurityConfiguration#httpSecurity()
先创建一个默认的密码管理器,
接下来进入authenticationBuilder.parentAuthenticationManager(authenticationManager());
,这里就是AuthenticationConfiguration
里面的处理。这个类后面和 springaop 加载我们写的注释单独在@EnableGlobalAuthentication
注解类说
接着创建 HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
,
.csrf(withDefaults()) 启用 CSRF 保护,创建
CsrfConfigurer
类,并添加入添加入AbstractConfiguredSecurityBuilder#configurers
.addFilter(new WebAsyncManagerIntegrationFilter())
这个有一个很有意思的类FilterOrderRegistration
。 前面问的根据 filter 是如何包装顺序的。就在这个类里面
|
|
springSecurity 事先使用这个类把预加载的类全部排序好,然后每次 add 一个新的 filter 就会使用这个里面的序号。如果我们有自定义的类,也要提前加载到里面去,不然就会
|
|
WebAsyncManagerIntegrationFilter
: 与处理异步 web 请求相关的实用程序方法
.exceptionHandling(withDefaults())
这个是异常的处理
提示: 这个里面每次添加一个类,如果在HttpSecurity
中调用getOrApply
。比如这个代码调用的是exceptionHandlingCustomizer.customize(getOrApply(new ExceptionHandlingConfigurer<>()));
。
打开ExceptionHandlingConfigurer
类,发现是一个HttpSecurityBuilder
, 这样只需要看configure
方法大概就能明白这个是一个什么类。
这个就是在 filter 中添加了一个ExceptionTranslationFilter
filter.主要就是SecurityConfigurer
的俩个方法。先调用init(B builder)
,然后configure(B builder)
后面都是一样,就跳过了
applyDefaultConfigurers(http);
这里的这一句,就是从"META-INF/spring.factories" 中加载并实例化给定类型的工厂实现
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader)
然后调用http.apply(configurer);
添加到configurers
里面
接下来回到SecurityFilterChainConfiguration
类
http.authorizeRequests().anyRequest().authenticated();
首先添加了http.authorizeRequests()
然后调用 return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context)).getRegistry();
。
先把ExpressionUrlAuthorizationConfigurer
放入 config 中,返回一个调用了getRegistry()
。
也就是ExpressionInterceptUrlRegistry
类。
后面调用的.anyRequest()
,也就是AbstractRequestMatcherRegistry#anyRequest()
。先了解一下结构图
完整调用链就是AbstractRequestMatcherRegistry#anyRequest()
-> AbstractRequestMatcherRegistry#requestMatchers(RequestMatcher... requestMatchers)
-> AbstractConfigAttributeRequestMatcherRegistry#chainRequestMatchers(List<RequestMatcher> requestMatchers)
-> ExpressionUrlAuthorizationConfigurer#chainRequestMatchersInternal(List<RequestMatcher> requestMatchers)
-> return new AuthorizedUrl(requestMatchers);
http.authorizeRequests().anyRequest().authenticated();
是需要所有请求登录后才能访问
authorizeRequests
是创建了一个ExpressionUrlAuthorizationConfigurer
并添加入 configurer 中。anyRequest
是创建了一个new AuthorizedUrl(requestMatchers)
,其中requestMatchers
是AnyRequestMatcher.INSTANCE;
也就是AnyRequestMatcher
对象。里面matches(HttpServletRequest request)
直接返回 tureauthenticated()
也就是授权,ExpressionInterceptUrlRegistry#addMapping
。里面放入了一个UrlMapping
,UrlMapping
的俩个属性,一个是AnyRequestMatcher
(所有请求),configAttrs
表示SecurityConfig
。SecurityConfig
的值为private static final String authenticated = "authenticated"
http.formLogin();
创建了一个FormLoginConfigurer
,也就是SecurityConfigurer
。关注init
和configure
方法。后面统一讲解
http.httpBasic();
HttpBasicConfigurer
类
http.build()
进行构建,这个就是非常重要的一个方法,build 对象,老规矩。进入AbstractConfiguredSecurityBuilder#doBuild()
方法
beforeInit();
: 还是没有什么
init()
: 调用里面所有的configurers
里面的init方法
,后面HttpSecurity#doBuild
统一讲解,先把流程捋一遍
接下来SecurityFilterChain
就已经创建好了,看一下里面的方法
|
|
肯定是先匹配,如果成功了,就返回里面所有的 filter 进行过滤,比如刚刚设置的所有请求需要登录,也还有我们需要排除的请求
SecurityAutoConfiguration
类就已经大致讲完了,
@EnableGlobalAuthentication
当前注解在@EnableSecurity
中会自动加上
@Import(AuthenticationConfiguration.class)
AuthenticationConfiguration
上面@Import(ObjectPostProcessorConfiguration.class)
。 以前使用的ObjectPostProcessor
就是在这里注入的,注入AutowireBeanFactoryObjectPostProcessor
对象
AuthenticationManagerBuilder
|
|
这里面返回了一个AuthenticationManagerBuilder
的 bean,也就是上面``
HttpSecurityConfiguration#httpSecurity()的时候需要的类,这个类也是一个
SecurityBuilder`。
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
首先创建了一个LazyPasswordEncoder
,就是PasswordEncoder
,用来管理密码的
AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
这个就是在SecurityAutoConfiguration
中创建的 springSecurity 的发布订阅,用来订阅事件
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder( objectPostProcessor, defaultPasswordEncoder);
就是AuthenticationManagerBuilder
的真正实现了。接下来回到getAuthenticationManager()
方法
|
|
AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
获取到DefaultPasswordEncoderAuthenticationManagerBuilder
|
|
需要注意的是,this.globalAuthConfigurers
就是上面三个类,
|
|
调用了apply
也就是add
方法。添加到configurers
中
然后调用build
并返回。 又是到了 doBuild()
这里
beforeInit();
没有
init();
上面三个类的init
方法
EnableGlobalAuthenticationAutowiredConfigurer#init
InitializeUserDetailsBeanManagerConfigurer#init
调用了auth.apply(new InitializeUserDetailsManagerConfigurer());
这个类比上面类名字少了一个 bean,并且没有后 init 方法 只有configure
方法。 里面创建的DaoAuthenticationProvider
,里面默认有一个passwordEncoder
,在无参构造方法里面。而UserDetailsService
和DaoAuthenticationProvider
是同一个,也就是在UserDetailsServiceAutoConfiguration#inMemoryUserDetailsManager
这里创建的。里面继承了,所以是同一个InitializeAuthenticationProviderBeanManagerConfigurer#init
跟 2 一样,apply 了一个InitializeAuthenticationProviderManagerConfigurer
beforeConfigure();
没有
configure();
调用里面的configure
方法
2: InitializeUserDetailsManagerConfigurer#configure
方法
|
|
获取所有UserDetailsService
和PasswordEncoder
和UserDetailsPasswordService
,使用DaoAuthenticationProvider
进行管理,然后添加到AuthenticationManagerBuilder#authenticationProviders
中
3: InitializeAuthenticationProviderManagerConfigurer#configure
方法,把 spring 中的所有AuthenticationProvider
添加到AuthenticationManagerBuilder#authenticationProviders
中
然后又到了熟悉的AuthenticationManagerBuilder#performBuild
|
|
首先使用ProviderManager
管理authenticationProviders
和parentAuthenticationManager
,这里的eraseCredentials
和CredentialsContainer
类有关,也就是敏感数据。接着的eventPublisher
就是发布订阅了,默认会创建的
然后providerManager = postProcess(providerManager);
就是注入 spring 容器中,接着返回
,这里返回的其实是ProviderManager
对象了,接着就是到了HttpSecurity
的创建了,后面HttpSecurity#doBuild()
时候再讲HttpSecurity
的构建
这里面的LazyPasswordEncoder
这个类也很有意思,手动制造一个懒加载类
@EnableGlobalMethodSecurity
这里有个很坑的地方,里面的prePostEnabled
,securedEnabled
这些属性,不是直接在GlobalMethodSecuritySelector
中进行处理的,放在了GlobalMethodSecuritySelector#methodSecurityMetadataSource
这个 bean 里面进行处理,然后开启prePostEnabled
之后,就会加载PrePostAnnotationSecurityMetadataSource
类。 这个我找了半天,后面无意中才发现
这个注解也添加了@EnableGlobalAuthentication
注解
主要是看GlobalMethodSecuritySelector
类
里面加载了AutoProxyRegistrar
,这个就是 springaop 的类,创建代理对象的一个类。会创建InfrastructureAdvisorAutoProxyCreator
类来创建代理对象。关键是GlobalMethodSecurityConfiguration
和MethodSecurityMetadataSourceAdvisorRegistrar
这俩个类
MethodSecurityMetadataSourceAdvisorRegistrar
类里面都用到了GlobalMethodSecurityConfiguration
。我就放在一起了
MethodSecurityMetadataSourceAdvisorRegistrar
这个类里面就是往 spring 中注册了一个MethodSecurityMetadataSourceAdvisor
对象
MethodSecurityMetadataSourceAdvisor
这个类就是PointcutAdvisor
,使用AbstractAutoProxyCreator
创建代理对象的是,会获取Pointcut
来判断是否需要代理对象,然后使用Advice
来进行其余操作。这是 springaop 的内容就不过多讲解了
aop 首先获取pointcut
,进行匹配,当前的为
|
|
也就是StaticMethodMatcherPointcut
,ClassFilter
默认都是 true,方法匹配为MethodSecurityMetadataSourceAdvisor#attributeSource
进行匹配。而methodSecurityMetadataSource
是在GlobalMethodSecurityConfiguration#methodSecurityMetadataSource
里面进行创建的。这俩个类后面讲。只要匹配成功就和 aop 一样流程了
这里的Advice
就是MethodSecurityInterceptor
类。在GlobalMethodSecurityConfiguration#methodSecurityInterceptor
中创建
MethodSecurityInterceptor
isPrePostEnabled
添加PrePostAnnotationSecurityMetadataSource
类,主要关注getAttributes
方法后面会讲。
这里面就是我们常用的注解了,然后构建成ConfigAttribute
并返回。里面的构建主要用的是PrePostInvocationAttributeFactory
的实现,只有一个实现
isSecuredEnabled
这个就是@Secured
注解的处理。 逻辑基本和上面一样
最后返回一个DelegatingMethodSecurityMetadataSource
对象,就是MethodSecurityInterceptor
中用到的对象
匹配成功的 aop 都会进入MethodSecurityInterceptor#invoke
|
|
这个一看就是标准 aop
super.beforeInvocation(mi)
这个里面就有授权了,Authorization
和authentication
不一样,一个是认证一个是授权。这个是授权,简单说就是角色
Collection
attributes = this.obtainSecurityMetadataSource().getAttributes(object);
接着来到了DelegatingMethodSecurityMetadataSource#getAttributes
|
|
this.methodSecurityMetadataSources
里面的值,就是GlobalMethodSecurityConfiguration#methodSecurityMetadataSource
里面的sources
.
构建出来返回attributes
。
Authentication authenticated = authenticateIfRequired();
这个就是获取当前认证信息
attemptAuthorization(object, attributes, authenticated);
使用accessDecisionManager
进行授权。放到MethodInterceptor
中进行讲解
里面还有授权失败发布事件publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));
接着就是授权成功发送事件,接着就是返回一个InterceptorStatusToken
对象
result = mi.proceed();
执行业务
super.finallyInvocation(token);
是否刷新InterceptorStatusToken
,前面传参是 false
return super.afterInvocation(token, result);
后处理器,与前处理器基本一样。剩下的MethodInterceptor
中进行讲解
MethodInterceptor
创建出MethodSecurityInterceptor
对象给MethodInterceptor
用也就是securityMetadataSource
属性
this.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor(): new MethodSecurityInterceptor();
一般都是MethodSecurityInterceptor
accessDecisionManager()
这个就是AbstractSecurityInterceptor#attemptAuthorization
的授权方法
|
|
前提条件必须开启prePostEnabled
这里面返回的也是AffirmativeBased
,有时候我们自定义也会使用这个,只要有一个AccessDecisionVoter
通过就认为是有权限的,这里就不过多讲解了
里面的GrantedAuthorityDefaults
对象,也可以是我们自定义的一个前缀,默认前缀为ROLE_
我们一般自定义的,会使用.accessDecisionManager(accessDecisionManager())
,在HttpSecurity#doBuild()
中进行讲解
afterInvocationManager
与上面前处理一样
methodSecurityMetadataSource
这个就是MethodSecurityMetadataSource
对象了
总结一下这里,就是实现 springaop 的AbstractPointcutAdvisor
对象MethodSecurityMetadataSourceAdvisor
, 进行 aop 加载,处理
EnableMethodSecurity
这个注解没有@EnableGlobalMethodSecurity
这么强大,代码基本跟@EnableGlobalMethodSecurity
一样
HttpSecurity#doBuild()
接下来是最后一块内容了,主要是看里面初始化,构建了哪些类
又是熟悉的AbstractConfiguredSecurityBuilder#doBuild()
首先看看我们一开始创建HttpSecurity
的时候添加了哪些类 HttpSecurityConfiguration#httpSecurity()
|
|
CsrfConfigurer
,ExceptionHandlingConfigurer
,HeadersConfigurer
,SessionManagementConfigurer
,SecurityContextConfigurer
,RequestCacheConfigurer
,AnonymousConfigurer
,ServletApiConfigurer
,DefaultLoginPageConfigurer
,LogoutConfigurer
还有我们添加到META-INF/spring.factories
中的AbstractHttpConfigurer.class
类
接着回到这里, 我们是自定义了一个SecurityFilterChain
,所以在这里面进行构建
首先 http.formLogin();
添加了FormLoginConfigurer
http.authorizeRequests()
添加了ExpressionUrlAuthorizationConfigurer
,这个只有configure
没有init
http.csrf()
添加了CsrfConfigurer
http.userDetailsService(userDetailsService())
添加了一个自定义的UserDetailsService
FormLoginConfigurer
也是一个SecurityConfigurer
首先创建对象给属性赋值
|
|
接着来到init
|
|
updateAuthenticationDefaults();
|
|
loginProcessingUrl(this.loginPage);
- 设置登录页面
this.authFilter
也就是UsernamePasswordAuthenticationFilter
的RequestMatcher
设置为new AntPathRequestMatcher(loginProcessingUrl, "POST")
failureUrl(this.loginPage + "?error");
- 设置失败页面
this.failureHandler
设置为new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl)
里面的authenticationFailureUrl
是/login + "?error"
getBuilder().getConfigurer(LogoutConfigurer.class);
就是前面加入的那一堆Configurer
中的一个.这个默认就是当前设置的值,不用理会
updateAccessDefaults(http);
里面默认为 false
registerDefaultAuthenticationEntryPoint(http);
获取上面Configurer
里面的ExceptionHandlingConfigurer
在ExceptionHandlingConfigurer
中有俩个属性, defaultEntryPointMappings
和defaultDeniedHandlerMappings
, 基本看注释就能知道是做什么的
,这个注释是 map 的 value 类上的注释
|
|
我们这里的是添加defaultDeniedHandlerMappings
, key
是RequestMatcher
,是否匹配。 value
是匹配成功就执行
这个类里面也是只有configure()
,没有init()
, 后面讲解
先说里面的value
,就是当前类的this.authenticationEntryPoint
也就是创建类时候的LoginUrlAuthenticationEntryPoint
key
就是AndRequestMatcher
,但是里面聚合了俩个RequestMatcher
, 一个是MediaTypeRequestMatcher
,还有一个是NegatedRequestMatcher
其实到了这一步,我们只需要了解其中一个,剩下的都大同小异了
举例: ExceptionHandlingConfigurer
ExceptionHandlingConfigurer
这个类就是刚刚在FormLoginConfigurer
中处理的那个,往这个里面添加了defaultEntryPointMappings
属性
然后我们找到ExceptionHandlingConfigurer
中的configure(H http)
方法, 里面就是创建了一个ExceptionTranslationFilter
过滤器,添加到了http
中, 代码是这一段
|
|
接着我们打开ExceptionTranslationFilter
,这就是一个Filter
,找到doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
方法,就是在处理catch (Exception ex) {
的时候,做的一些事情,接着继续打开handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, RuntimeException exception)
方法,
|
|
发现最后还是到了
|
|
里面就有this.authenticationEntryPoint.commence(request, response, reason);
这段代码,
创建的是DelegatingAuthenticationEntryPoint
|
|
基本上流程就完成了,请求先走过滤器,然后走不同的 filter,报错就到了这一步,进行错误处理,其余都基本一致了
备注
剩下的一些处理基本和上面这个流程一致,还有几个注解需要注意下
@CsrfToken
@CurrentSecurityContext
@AuthenticationPrincipal
这三个注解是在WebMvcSecurityConfiguration
类进行处理的,只要启动了@EnableWebSecurity
注解,就会启动
|
|
使用方法
|
|
里面的SpringWebMvcImportSelector
类注入了WebMvcSecurityConfiguration
,这就是 springmvc 中HandlerMethodArgumentResolver
的处理,也就是参数的处理,比如我们添加的@PathVariable
,@RequestBody
等,都是HandlerMethodArgumentResolver
的实现类处理的,当然还有HandlerMethodReturnValueHandler
,这些就是DispatcherServlet
里面的处理了
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/geek/post/code/docs/SpringSecurity/SpringSecurity%E6%B5%81%E7%A8%8B%E8%A1%A5%E5%85%85/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com