spring security 源码流程图
2023-10-18 15:59:26 0 举报
spring security 源码流程图
作者其他创作
大纲/内容
FilterSecurityInterceptor
用户认证信息验证用户认证信息非空且不是匿名访问则验证通过
this.getAuthenticationManager().authenticate(authRequest)
同一个方法这是其中的一些属性、处理器等不一样罢了这里的 SecurityMetadataSource 是下面的
authenticateIfRequired()
getAdvice()
创建 HttpSecurity
核心操作,会验证 cookieName 来判断是否需要执行 remember me 操作,验证通过后进行自动登录,登录信息会保存到配置的数据源中
chain.getFilters()
加载用户信息
SimpleUrlAuthenticationFailureHandler#onAuthenticationFailure
springSecurityFilterChain()
targetBeanName 就是 springSecurityFilterChain从 单例bean池中获取到该beanName 对应的 Bean - FilterChainProxy 并赋值给 delegate 属性
doFilter封装到 FilterInvocation
存储上下文根据策略进行存储,默认为HttpSessionSecurityContextRepository使用http session作为存储器
使用方式 - 一般使用 font color=\"#e74f4c\
MethodSecurityMetadataSourcePointcut
用户认证成功
if (fi.getRequest() != null && observeOncePerRequest)该请求第一次进来需要给它设置一些属性以便同一请求第二次进来可以直接进入下一个 Filter
提供 filters
添加到 securityFilterChains 中
securityInterceptor.setSecurityMetadataSource(metadataSource)
this.tokenRepository.loadToken(request)
!this.requireCsrfProtectionMatcher.matches(request)
apply(new DaoAuthenticationConfigurer<>( userDetailsService))
if (!provider.supports(toTest))校验 AuthenticationProvider
获取 access 访问表达式
配置及初始化
用户前置检查
doFilter
doFilter加载上下文信息
获取所有的 security 需要用到的过滤器
会话管理(Session)过滤器
webSecurityConfigurers.add(entry.getValue())
foreach 执行完成if (result == null && parent != null)如果返回的认证信息为空且有父认证管理器则去执行父认证管理器的认证操作
用户后置检查
web.addSecurityFilterChainBuilder(http)
request.getHeader(csrfToken.getHeaderName())
performBuild()
!csrfToken.getToken().equals(actualToken)
创建 Security 的 Filter
getHttp()
走一遍 bean 初始化开始的生命周期
OAuth2ImportSelector
http.addFilter(filter)
AbstractInterceptUrlConfigurer#configure
init()
创建
不一致
SessionManagementFilter
WebSecurityEnablerConfiguration
beanFactory .getBeansOfType(WebSecurityConfigurer.class)
extends
AbstractInterceptUrlConfigurer#createFilterSecurityInterceptor
1. WebSecurity
从请求中获取用户名和密码
默认项目中是通常来说是不会用的
UserDetailsServiceAutoConfiguration
返回认证成功的Authentication否则就是直接抛异常了
这里会将HttpSecurity配置的所有SecurityConfigurer都添加到 configurers 属性(该属性在父类 AbstractConfiguredSecurityBuilder),后面会执行每个 SecurityConfigurer 的configure 方法去创建对应的 b style=\
WebSecurity 没有实现
AOP 方式拦截 controller 上有对应访问授权注解的方法
LazyCsrfTokenRepository - HttpSessionCsrfTokenRepository 保存 csrf token
2. 请求进来执行过滤器
SecurityProperties
用户登录认证
org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer#insertSpringSecurityFilterChain
MethodSecurityMetadataSourceAdvisorRegistrar
执行session 失效
configure(localConfigureAuthenticationBldr)
WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder#userDetailsService
getFilters(fwRequest)
AbstractConfiguredSecurityBuilder#doBuild
com.example.demo.service.impl.UserServiceImpl#loadUserByUsername
this.tokenRepository.generateToken(request)
new DefaultWebSecurityExpressionHandler()
对 OAuth2.0 开放授权协议进行支持
UsernamePasswordAuthenticationFilter#attemptAuthentication
前置处理
MethodSecurityMetadataSourceAdvisor
MethodSecurityInterceptor#invoke
自动装配条件,实际使用自定义WebSecurityConfigurerAdapter这个Bean@ConditionalOnBean(WebSecurityConfigurerAdapter.class)@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)没有自定义 WebSecurityConfigurerAdapter 的bean 则使用这个配置类但是实际使用都是会自定义这个 bean 的,所以这个配置类一般不会装配
session 认证默认 NullAuthenticatedSessionStrategy 不做任何操作
过滤器级别拦截授权
认证失败捕获异常长执行认证失败处理
HttpSecurity.build()
获取到的用户信息添加到缓存
authenticationManager()
securityInterceptor.setAccessDecisionManager(getAccessDecisionManager(http))
new AffirmativeBased(getDecisionVoters(http))
for (SecurityFilterChain chain : filterChains)
2. HttpSecurity
获取 HttpSecurity 的 configures也就是创建时添加的所有SecurityConfigurer
REGISTRY .createRequestMap()
WebSecurity#performBuild
initDelegate(wac)
校验 fi 是不是FilterInvocation 不是就会抛异常
请求中没有的话就去生成一个
决策通过允许授权,最终返回InterceptorStatusToken
创建授权决策的投票者AccessDecisionVoter
GlobalMethodSecurityConfiguration
用户认证信息验证通过执行 session 认证策略
objectPostProcessor .postProcess(new WebSecurity(objectPostProcessor))
this.getUserDetailsService().loadUserByUsername(username)
securityContextRepository.containsContext(request)
registerBeanDefinitions并给构造参数赋值
创建 FilterSecurityInterceptor
执行 createMetadataSource(http) 方法创建 FilterInvocationSecurityMetadataSource 对象
创建 WebSecurity 并赋值给webSecurity 属性
this.obtainSecurityMetadataSource() .getAttributes(object)
ExpressionUrlAuthorizationConfigurer#getDecisionVoters
DefaultFilterInvocationSecurityMetadataSource#getAttributes
密码校验
authenticationBuilder.parentAuthenticationManager(authenticationManager)
方法授权拦截过滤器
AutowiredWebSecurityConfigurersIgnoreParents
Security Filter Chain 执行排序按照 FilterComparator
bean 生命周期
new WebExpressionVoter()
为 FilterChainProxy 创建一个包装类DelegatingFilterProxyfilterName 是 springSecurityFilterChain - 是 FilterChainProxy 的 beanName
缓存没有则去获取
web.postBuildAction(() -> { FilterSecurityInterceptor securityInterceptor = http .getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); });
WebExpressionVoter#vote
authenticationDetailsSource 默认使用WebAuthenticationDetailsSource
postAuthenticationChecks.check(user)
AffirmativeBased#decide
configurer.configure((B) this)这里的 this 是 HttpSecurity
创建认证成功的用户认证信息实际就是创建一个新的UsernamePasswordAuthenticationToken对象并设置 authenticated 认证属性为 true
FormLoginConfigurer#init#configure
创建 VirtualFilterChain
缓存认证管理器构建器
SecurityProperties 默认 登录的用户名 user密码 UUID 随机配置文件自行配置
用户认证失败
触发用户认证成功事件
后置处理
provider 默认是DaoAuthenticationProvider添加到 AuthenticationManagerBuilder 中
服务器 csrf token 不为空则执行如下处理
认证管理器为 ProviderManager
创建 AccessDecisionManagerSpring security默认使用的是AffirmativeBased可以通过 http.authorizeRequests().accessDecisionManager(自定义)来进行自定义
preAuthenticationChecks.check(user)
MyWebSecurityConfig#configure(HttpSecurity)
getWebSecurityConfigurers
获取到的是我们自己定义的 b style=\
@Bean MethodInterceptor设置了前置、后置处理器、SecurityMetadataSource
authenticationManager.authenticate(rememberMeAuth)
自定义 MyWebSecurityConfig@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceImpl);}
catch AuthenticationException执行登录失败
默认请求必须是 post
封装认证请求 - request 中的一些其它的信息
LogoutFilter
添加获取到的 HttpSecurity 到 WebSecurity 的securityFilterChainBuilders 属性中
HttpSecurity#performBuild
aop 进入方法拦截器
认证失败处理
WebSecurity 中的configurers 属性是 MyWebSecurityConfig
doFilterInternalLazyCsrfTokenRepository - HttpSessionCsrfTokenRepository 加载加载服务器保存的 csrf token
entry.getValue()
进入自定义的MyWebSecurityConfig
在 FilterChainProxy 这个过滤器内部执行security filter 链路
authentication != null && !trustResolver.isAnonymous(authentication)
localConfigureAuthenticationBldr认证管理构建器添加 SecurityConfigurer
doFilter路径匹配是否是登出创操作
获取认证通过的用用户认证信息 Authentication
WebSecurityConfigurerAdapter#configure(WebSecurity)
认证成功处理
SecurityContextPersistenceFilter
new FilterChainProxy(securityFilterChains)
SecurityContextHolder.getContext() .getAuthentication()
Security Filter 中一些属性好多都是可以通过 HttpSecurity 里的方法先获取 SecurityConfigurer 再调用里面的方法来自定义
access 表达式处理器SecurityExpressionHandler 设置
this.userCache.getUserFromCache(username)
RememberMeAuthenticationFilter
WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity>
webSecurity.build()
SecurityContextHolder.getContext().setAuthentication(rememberMeAuth)
这一块逻辑都是 MyWebSecurityConfig extends WebSecurityConfigurerAdapter的逻辑
configure(http)
for (SecurityBuilder securityFilterChainBuilder : securityFilterChainBuilders)
获取认证管理器
for (AuthenticationProvider provider : getProviders())
将用户名和密码添加到UsernamePasswordAuthenticationTokenprincipal 主体为 usernamecredentials 凭证为 password并设置为未认证 setAuthenticated(false)
在自定义的 WebSecurityConfig extends WebSecurityConfigurerAdapter中给认证管理器配置 UserDetailsService 为自定义的@Autowiredprivate UserServiceImpl userServiceImpl;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceImpl);}
SpringBootWebSecurityConfiguration
@EnableGlobalMethodSecurity
存储上下文信息
处理登出操作默认是 SecurityContextLogoutHandler
@import
SessionManagementConfigurer#init#configure初始化 SecurityContextRepository、SessionAuthenticationStrategy、InvalidSessionStrategy
authRequest.setDetails(authenticationDetailsSource.buildDetails(request))
new ExpressionUrlAuthorizationConfigurer(context)
SpringWebMvcImportSelector
super.beforeInvocation(mi)
依赖注入 @Value(\"#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}\
保存到 request 中
遍历所有的SecurityConfigurer 执行 configure 方法这里的this 是 HttpSecurity如果是在 WebSecurity进来那就是 WebSecurity
上下文信息保存到线程中
request.getParameter(csrfToken.getParameterName())
扩展方法,认证成功后的操作
provider.authenticate(authentication)
1. bean 初始化
DelegatingFilterProxy#afterPropertiesSet
CSRF 跨站请求伪造过滤器
请求第一次进来执行过滤操作进入 else 逻辑
先从缓存里取用户信息 UserDetails
InMemoryUserDetailsManager
builder.authenticationProvider(provider)
登出过滤器
@Bean
注册过滤器
getPointcut()
controller 执行完成返回后执行后置处理
HttpSessionSecurityContextRepository
new DelegatingFilterProxy(filterName)
默认使用内存级别
DelegatingPasswordEncoder
if (result != null)认证成功后将原始的认证信息中的details 复制到认证成功创建的新的认证信息
for (AccessDecisionVoter voter : getDecisionVoters())投票决策
扩展方法,认证失败后的操作
obtainUsername(request)obtainPassword(request)
手动执行 bean 初始化开始的生命周期
服务器csrf token为空则执行如下处理
getAccessDecisionManager(http)获取授权决策管理器
和 FilterSecurityInterceptor进入一样的方法
postProcess(provider)
parent.authenticate(authentication)
disableLocalConfigureAuthenticationBldr 设置为 true
AbstractSecurityInterceptor#beforeInvocation
@Import
WebSecurityConfigurerAdapter
session 策略执行
AuthenticationConfiguration
PrePostAnnotationSecurityMetadataSourceAbstractFallbackMethodSecurityMetadataSource
postProcess(result)
设置父级认证管理器
用户认证信息验证失败执行session 失效策略
构建认证管理器并缓存缓存认证管理器
执行 doBuild 操作
前置处理,进行授权校验
super.beforeInvocation(fi)
postBuildAction.run()
DaoAuthenticationProvider#retrieveUser
返回 FilterChainProxy
filter 添加到 HttpSecurity 的 filters属性中
ChannelProcessingFilter -> ConcurrentSessionFilter -> WebAsyncManagerIntegrationFilter -> ... -> UsernamePasswordAuthenticationFilter -> ... -> DefaultLoginPageGeneratingFilter -> DefaultLogoutPageGeneratingFilter -> ... -> ExceptionTranslationFilter -> FilterSecurityInterceptor -> SwitchUserFilter当然也可以自定义 Filter 添加到这个 chain 中,使用 HttpSecurity#addFilter(Filter) 方法下面我就画了几个 Filter 的流程,没有全部画
webSecurity.apply(webSecurityConfigurer)
1. WebSecurity #performBuild
invoke(fi)
repo.loadContext(holder)
expressionVoter.setExpressionHandler(getExpressionHandler(http))
SecurityFilterAutoConfiguration
requestMap
beforeConfigure()
AuthenticationManagerBuilder#performBuild
SecurityContextHolder.setContext(contextBeforeChainExecution)
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain()
将 MyWebSecurityConfig添加到 WebSecurity 对象的 configurers 属性
2个token 是否一致,一致则通过,不一致则抛错
SecurityAutoConfiguration
session 有效且存在上下文信息则去取Authentication 认证信息
AutowireBeanFactoryObjectPostProcessor给手动创建的对象手动执行 bean 创建后的生命周期AbstractAutowireCapableBeanFactory#initializeBean - 初始化、BeanPostProcessor等AbstractAutowireCapableBeanFactory#autowireBean - 依赖注入DisposableBean、SmartInitializingSingleton
if (entry.getKey().matches(request))执行 RequestMatcher 的matches 方法进行匹配,request匹配通过则取RequestMatcher 对应的访问表达式
继续执行后面的过滤器 - DispatcherServlet - controller
DaoAuthenticationConfigurer extends AbstractDaoAuthenticationConfigurer#configure
initFilterBean
configure()
获取 MethodInterceptorbeanName 在 注册bean 的时候在构造参数中赋值进来 methodSecurityInterceptor
从认证管理器配置类中获取
用户认证
securityFilterChains.add(securityFilterChainBuilder.build())
AffirmativeBased的决策逻辑是: 1. 只要有AccessDecisionVoter的投票为 ACCESS_GRANTED则同意用户进行访问2. 如果全部弃权也表示不通过,抛出AccessDeniedException3. 如果没有一个人投赞成票,但是有人投反对票,则将抛出AccessDeniedException
FilterChainProxy#doFilter
默认路径 - post /login@see UsernamePasswordAuthenticationFilter 构造方法自定义 loginProcessingUrl@see FormLoginConfigurer#createLoginProcessingUrlMatcher
ExpressionUrlAuthorizationConfigurer#createMetadataSource
GlobalMethodSecuritySelector
没有则从参数中取parameterName: _csrf
WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder extends AbstractConfiguredSecurityBuilder也会去执行 doBuild 方法
SecurityDataConfiguration
getConfigurers()
执行用户认证
@EnableGlobalAuthentication
postProcess(providerManager)
AbstractUserDetailsAuthenticationProvider#authenticate
AbstractAuthenticationProcessingFilter
认证成功执行认证成功处理
配置 WebSecurity注册 springSecurityFilterChain
登出成功后的逻辑
对filter chain 进行排序根据 FilterComparator后续执行顺序也是按照这个排序执行的
创建 SecurityConfigurer 添加到 HttpSecurity 的 configures 中
doFilter通过路径匹配判断是否需要用户认证匹配成功则进行用户认证,匹配失败则进入下一个 Filter
DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder这里会创建认证管理器 ProviderManager
记住我过滤器
方法在父类
配置认证相关的核心类,负责生成AuthenticationManager
匹配成功进行用户认证
setFilterChainProxySecurityConfigurer
DefaultSecurityFilterChain#getFilters
判断是否需要进行 csrf 保护校验@see CsrfFilter.DefaultRequiresCsrfMatcher
DefaultConfigurerAdapter
2. 在 WebSecurity 的performBuild 方法中获取到 HttpSecurity 后执行 HttpSecurity #performBuild
AuthenticationManagerBuilder#userDetailsService
SavedRequestAwareAuthenticationSuccessHandler#onAuthenticationSuccess
配置类,只要还是给默认的InMemoryUserDetailsManager 用户认证信息用的
doFilter自动登录
CsrfFiltertokenRepository属性LazyCsrfTokenRepository 委托-> HttpSessionCsrfTokenRepository
创建请求匹配器和授权表达式的映射
需要进行 csrf 保护校验则获取请求中的 csrf token 和服务器的比较请求头中取 csrf tokenheaderName: X-CSRF-TOKEN
if (!getSecureObjectClass().isAssignableFrom(object.getClass()))
spring-boot-autoconfigure-2.3.2.RELEASE.jar!\\META-INF\\spring.factories
mi.proceed()
if (postOnly && !request.getMethod().equals(\"POST\"))
authenticationBuilder 属性值为new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilderfont color=\"#262626\
new FilterSecurityInterceptor()
PasswordEncoderFactories维护了前缀-PasswordEncoder 映射关系
自定义实现
默认是 SimpleUrlAuthenticationFailureHandler
如果 http.rememberMe 设置了AuthenticationSuccessHandler则执行该方法
使用自定义的 UserDetailsService加载用户信息
入口
securityInterceptor.setAuthenticationManager(authenticationManager)
filters.sort(comparator)
SimpleRedirectInvalidSessionStrategy#onInvalidSessionDetected
WebSecurityConfiguration
controller 执行完成返回进行后置处理FilterSecurityInterceptor 没有设置该处理器,所以可暂时忽略
执行认证
默认是 SavedRequestAwareAuthenticationSuccessHandler
localConfigureAuthenticationBldr.build()
authenticationConfiguration .getAuthenticationManager()
登录时会调用 b style=\
WebSecurityConfigurerAdapter#init
ProviderManager#authenticate
在 HttpSecurity 执行 doBuild 方法中的 configure 方法时会去执行 ExpressionUrlAuthorizationConfigurer 的 configure 方法
failureHandler 和 successHandler 可看下 springsecurity 内置的实现类修改它们的默认值:子类重写 WebSecurityConfigurerAdapter#configure(HttpSecurity http) 方法http.formLogin()// 这里的 handler 是我自定义的.successHandler(new MyAuthenticationSuccessHandler(\"/main.html\")).failureHandler(new MyAuthenticationFailureHandler(\"/error.html\"))
createDefaultAccessDecisionManager(http)
REGISTRY 数据来源http.authorizeRequests() .antMatchers(\"/user/login\
如果存在 DispatcherServlet 则装配 WebMvcSecurityConfiguration
lambda 表达式用于创建 FilterSecurityInterceptor
在自定义的配置类中重写configure(HttpSecurity http) 方法执行HttpSecurity#authorizeRequests()
用户认证信息保存到线程中
this.userCache.putUserInCache(user)
进行授权决策
super
0 条评论
下一页