前言
Spring Boot Security提供了:
- 认证(Authentication)
 - 授权(Authorization)
 - 提供常见攻击保护:Cors、
 
架构
`Spring Boot Security架构
 是基于Servlet中的Filter实现的。整个Security是通过过滤器链(FilterChain)来完成认知、授权、攻击保护。
认证
认证管理(AuthenticationManager):
- 根据传入
Authentication,通过AuthenticationProvider来完成用户资源权限 - 根据用户认证情况,如果失败,发送publishAuthenticationFailure。如果成功,发送publishAuthenticationSuccess
 
认证提供者:AuthenticationProvider
- 根据传入Authentication,通过
UserDetailsService获取到UserDetails - 根据UserDetails通过
PassswordEncorder来完成密码校验 - 通过
UserDetailsChecker来完成用户前置校验。如(用户是否禁用,是否被锁,凭证是否过期)。可选方法 
用户信息服务:UserDetailsService
- 根据传入username获取到
UserDetails 
决策
安全拦截器:AbstractSecurityInterceptor
- 获取安全元数据资源
SecurityMetadataSource,一般是URL等信息 - 通过
AuthencationManager来获得Authentication,如果已存在则从上下文获取。 - 通过
AccessDecisionManger来决策,是否通过。 - 实现类FilterSecurityInterceptor实现了
Filter。 
访问决策管理:AccessDecisionManager
- 通过AccessDecisionVoter中的
vote投票决定是否通过。 
访问决策投:AccessDecisionVoter
- 通过投票决定,返回拒绝访问(ACCESS_DENIED,-1)、弃权访问(ACCESS_DENIED,0)、准许访问(ACCESS_GRANTED,1);
 
Spring Security Web
AuthenticationFilter:认证过滤器
- 通过
RequestMatcher校验当前请求是否需要执行当前过滤器。 - 通过
AuthenticationManagerResolver获取AuthenticationManager - 通过
AuthenticationManger获取Authentication。完成用户认证和获取用户信息 - 如果认证失败:通过
AuthenticationFailureHandler返回失败信息 - 如果认知成功:通过
AuthenticationSuccessHandler返回成功信息 
类似AuthenticationFilter:功能类AbstractAuthenticationProcessingFilterBasicAuthenticationFilter:继承OncePerRequestFilterDigestAuthenticationFilter:判断请求头:Authorization信息是否存在且以Digest 开头AbstractPreAuthenticatedProcessingFilter:当前类是通过判断是否存在认证信息,判断是否执行
注意:OncePerRequestFilter,当前过滤器在一个Session只执行一次。
LogoutFilter:退出登录过滤器
- 通过
SecurityContext获取上下文存储的Authentication信息。 - 执行
LogoutHandler中的logout完成,退出逻辑处理。 - 退出处理完成之后,执行
LogoutSuccessHandler中onLogoutSuccess方法。完成退出成功后的处理 
DefaultLoginPageGeneratingFilter:默认登录页通用过滤器
- 通过
doFilter完成校验请求链接是否是:loginError,logoutSuccess,isLoginUrlRequest,跳转到指定链接中。且为GET请求。 - loginPageUrl 登录页面URL
 - logoutSuccessUrl:登录成功URL
 - failureUrl:登录失败URL
 - formLoginEnabled: 表单登录是否启动
 
DefaultLogoutPageGeneratingFilter:默认登录退出通用页面
- 判断链接是否是
/logout,且为GET请求 - 是跳转到退出登录页面
 
一、Spring Boot Security启动原理
自动配置类
Spring Boot AutoConfiguration自动加载Security
Spring Boot AutoConfiguration项目下“META-INF/spring.factories"
提供了支持Servlet和Reactive两种Web类型的支持
#Security自动配置基于Servletorg.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
#UserDetailsService自动配置基于Servletorg.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
#安全过滤器自动配置基于Servletorg.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(安全自动配置)
- 完成认证异常类,ApplicationEventPublisher
 - 通过引入类:SpringBootWebSecurityConfiguration,WebSecurityEnablerConfiguration,SecurityDataConfiguration。
 
完成自动配置
@Configuration(proxyBeanMethods=false)/**
* 认证异常类的注册
*/@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)@EnableConfigurationProperties(SecurityProperties.class)@Import({SpringBootWebSecurityConfiguration.class,WebSecurityEnablerConfiguration.class,SecurityDataConfiguration.class})publicclassSecurityAutoConfiguration{@Bean@ConditionalOnMissingBean(AuthenticationEventPublisher.class)publicDefaultAuthenticationEventPublisherauthenticationEventPublisher(ApplicationEventPublisher publisher){returnnewDefaultAuthenticationEventPublisher(publisher);}}
SpringBootWebSecurityConfiguration(SpringBootWeb安全认证配置)
如果有一个bean类型是
WebSecurityConfigurationAdapter, 添加·EnableWebSecurity·注解。主要将确保注解是存在的当为默认安全自动配置。而且如果用户添加了定制安全且忘记添加了当前注解。
如果EnableWebSecurity已经添加了Bean或如果一个bean的名称是BeanIds#SPRING_SECURITY_FILTER_CHAIN在当前用户的bean配置中,将不使用当前配置。
@Configuration(proxyBeanMethods=false)//Web安全配置适配器@ConditionalOnClass(WebSecurityConfigurerAdapter.class)@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)//当Web类型为Servlet时生效@ConditionalOnWebApplication(type=Type.SERVLET)publicclassSpringBootWebSecurityConfiguration{@Configuration(proxyBeanMethods=false)@Order(SecurityProperties.BASIC_AUTH_ORDER)staticclassDefaultConfigurerAdapterextendsWebSecurityConfigurerAdapter{}}
WebSecurityConfigurerAdapter(Web安全配置适配器)
- ContentNegotiationStrategy:内容协商策略
 - AuthenticationConfiguration:认证配置
 - AuthenticationManagerBuilder:认证管理器构建
 - AuthenticationManager:认证管理
 - FilterSecurityInterceptor:过滤器安全拦截器
 - HttpSecurity:Http安全
 
@Order(100)publicabstractclassWebSecurityConfigurerAdapterimplementsWebSecurityConfigurer<WebSecurity>{privatefinalLog logger;privateApplicationContext context;/**
    * 内容协商策略,决定媒体类型
    * 请求头内容协商测试:accept
    * 如果accept为空则,通过所有媒体类型,否则通过指定媒体类型。
    * contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
    * 
    */privateContentNegotiationStrategy contentNegotiationStrategy;/**
    * 对象后置处理器
    */privateObjectPostProcessor<Object> objectPostProcessor;/**
    * 认证配置
    * 
    * 原子性Boolean,用来保证构建认证管理,不会因多线程导致创建多个
    * AtomicBoolean buildingAuthenticationManager = new AtomicBoolean();
    * ApplicationContext applicationContext;
    * 认证管理器
    * 通过bean类中AuthenticationManagerBuilder创建
    * AuthenticationManager authenticationManager;
    * 认证管理器是否已经初始化
    * boolean authenticationManagerInitialized;
    * List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections.emptyList();
    * 对象后置处理器
    * ObjectPostProcessor<Object> objectPostProcessor;
    */privateAuthenticationConfiguration authenticationConfiguration;/**
    * 认证管理器构建:用户获取用户认证管理
    *
    * 认证管理
    * AuthenticationManager parentAuthenticationManager;
    * 认证提供者
    * List<AuthenticationProvider> authenticationProviders = new ArrayList();
    * 用户详情服务
    * UserDetailsService defaultUserDetailsService;
    * 
    * 删除凭据(资源)
    * Boolean eraseCredentials;
    * 认证事件发布:一般为异常事件需要发布通知
    * AuthenticationEventPublisher eventPublisher;
    */privateAuthenticationManagerBuilder authenticationBuilder;/**
    */privateAuthenticationManagerBuilder localConfigureAuthenticationBldr;//禁用本地配置认证构建者privateboolean disableLocalConfigureAuthenticationBldr;//认证管理器是否已初始化privateboolean authenticationManagerInitialized;/**
    * 认证管理: 完成用户资源认证通过 AuthenticationProvider
    * 尝试去认证通过Authentication对象,返回一个完全数据Authentication对象(包括允许权限)
    * A AuthenticationManager在下列情况下必须抛出异常
    * DisabledException:当前账号禁用时,抛出异常
    * LockedException:当账号被锁定时,抛出异常
    * BadCredentialsException:提供了不正确的凭据。向上抛出异常是可选的。
    * 异常应该能别测试,且如果应用抛出异常应该按顺序抛出(如果账号别禁用或锁定,认证亲戚应该立即别拒绝,并在不执行凭据测试)。这是防止对禁用或锁定账号进行测试凭据。
    */privateAuthenticationManager;privateAuthenticationTrustResolver trustResolver;/**
    * Http安全预防工具
    * final HttpSecurity.RequestMatcherConfigurer requestMatcherConfigurer;
    * 过滤器列表
    * List<Filter> filters = new ArrayList();
    * 请求匹配器,默认匹配所有请求
    * RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
    * 过滤器比较器
    * FilterComparator comparator = new FilterComparator();
    */privateHttpSecurity http;privateboolean disableDefaults;
初始化,HttpSecurity前置构建行动
完成FilterSecurityInterceptor,过滤安全拦截器。完成对访问决策资源的验证
publicvoidinit(WebSecurity web)throwsException{HttpSecurity http=this.getHttp();
        web.addSecurityFilterChainBuilder(http).postBuildAction(()->{FilterSecurityInterceptor securityInterceptor=(FilterSecurityInterceptor)http.getSharedObject(FilterSecurityInterceptor.class);
            web.securityInterceptor(securityInterceptor);});}
获取HttpSecurity,在这个完成整个HttpSecurity的配置,包括登录、退出等。
protectedfinalHttpSecuritygetHttp()throwsException{if(this.http!=null){returnthis.http;}else{AuthenticationEventPublisher eventPublisher=this.getAuthenticationEventPublisher();this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);AuthenticationManager authenticationManager=this.authenticationManager();this.authenticationBuilder.parentAuthenticationManager(authenticationManager);Map<Class<?>,Object> sharedObjects=this.createSharedObjects();this.http=newHttpSecurity(this.objectPostProcessor,this.authenticationBuilder, sharedObjects);if(!this.disableDefaults){((HttpSecurity)((DefaultLoginPageConfigurer)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)this.http.csrf().and())//跨站点请求伪造.addFilter(newWebAsyncManagerIntegrationFilter())//Web异步管理拦截器过滤.exceptionHandling().and())//异常类过滤器设置.headers().and())//头过滤器设置.sessionManagement().and())//会话管理.securityContext().and())//安全上下文.requestCache().and())//请求缓存.anonymous().and())//匿名.servletApi().and())//api.apply(newDefaultLoginPageConfigurer())).and())//登录配置.logout();//退出登录ClassLoader classLoader=this.context.getClassLoader();List<AbstractHttpConfigurer> defaultHttpConfigurers=SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);Iterator var6= defaultHttpConfigurers.iterator();while(var6.hasNext()){AbstractHttpConfigurer configurer=(AbstractHttpConfigurer)var6.next();this.http.apply(configurer);}}this.configure(this.http);returnthis.http;}}
UserDetailsServiceAutoConfiguration(用户详情服务自动配置)
在所有Bean中未找到下列Bean则,加载当前配置
- AuthenticationManager:认证管理
 - AuthenticationProvider:认证提供者
 - UserDetailsService:用户详情服务
 
@Configuration(proxyBeanMethods=false)@ConditionalOnClass(AuthenticationManager.class)@ConditionalOnBean(ObjectPostProcessor.class)@ConditionalOnMissingBean(
		value={AuthenticationManager.class,AuthenticationProvider.class,UserDetailsService.class},
		type={"org.springframework.security.oauth2.jwt.JwtDecoder","org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector"})publicclassUserDetailsServiceAutoConfiguration{privatestaticfinalString NOOP_PASSWORD_PREFIX="{noop}";privatestaticfinalPattern PASSWORD_ALGORITHM_PATTERN=Pattern.compile("^\\{.+}.*$");privatestaticfinalLog logger=LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);@Bean@ConditionalOnMissingBean(
			type="org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")@LazypublicInMemoryUserDetailsManagerinMemoryUserDetailsManager(SecurityProperties properties,ObjectProvider<PasswordEncoder> passwordEncoder){SecurityProperties.User user= properties.getUser();List<String> roles= user.getRoles();returnnewInMemoryUserDetailsManager(User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build());}privateStringgetOrDeducePassword(SecurityProperties.User user,PasswordEncoder encoder){String password= user.getPassword();if(user.isPasswordGenerated()){
			logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));}if(encoder!=null|| PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()){return password;}return NOOP_PASSWORD_PREFIX+ password;}}
HttpSecurity
通过这个类,配置HttpSecurity。完成整个Filter链的构建。请求匹配,过滤比较.
配置示例
HttpSecurity http=newHttpSecurity(this.objectPostProcessor,this.authenticationBuilder, sharedObjects);if(!this.disableDefaults){((HttpSecurity)((DefaultLoginPageConfigurer)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)
      http.csrf().and())//跨站点请求伪造.addFilter(newWebAsyncManagerIntegrationFilter())//Web异步管理拦截器过滤.exceptionHandling().and())//异常类过滤器设置.headers().and())//头过滤器设置.sessionManagement().and())//会话管理.securityContext().and())//安全上下文.requestCache().and())//请求缓存.anonymous().and())//匿名.servletApi().and())//api.apply(newDefaultLoginPageConfigurer())).and())//登录配置.logout();//退出登录






