Spring Boot Security(身份认证和请求授权)

2022年9月29日13:14:42

前言

Spring Boot Security提供了:

  1. 认证(Authentication)
  2. 授权(Authorization)
  3. 提供常见攻击保护:Cors、

架构

`Spring Boot Security架构
是基于Servlet中的Filter实现的。整个Security是通过过滤器链(FilterChain)来完成认知、授权、攻击保护。

认证

认证管理(AuthenticationManager):

  1. 根据传入Authentication,通过AuthenticationProvider来完成用户资源权限
  2. 根据用户认证情况,如果失败,发送publishAuthenticationFailure。如果成功,发送publishAuthenticationSuccess

认证提供者:AuthenticationProvider

  1. 根据传入Authentication,通过UserDetailsService获取到UserDetails
  2. 根据UserDetails通过PassswordEncorder来完成密码校验
  3. 通过UserDetailsChecker来完成用户前置校验。如(用户是否禁用,是否被锁,凭证是否过期)。可选方法

用户信息服务:UserDetailsService

  1. 根据传入username获取到UserDetails

决策

安全拦截器:AbstractSecurityInterceptor

  1. 获取安全元数据资源SecurityMetadataSource,一般是URL等信息
  2. 通过AuthencationManager来获得Authentication,如果已存在则从上下文获取。
  3. 通过AccessDecisionManger来决策,是否通过。
  4. 实现类FilterSecurityInterceptor实现了Filter

访问决策管理:AccessDecisionManager

  1. 通过AccessDecisionVoter中的vote投票决定是否通过。

访问决策投:AccessDecisionVoter

  1. 通过投票决定,返回拒绝访问(ACCESS_DENIED,-1)、弃权访问(ACCESS_DENIED,0)、准许访问(ACCESS_GRANTED,1);

Spring Security Web

AuthenticationFilter:认证过滤器

  1. 通过RequestMatcher校验当前请求是否需要执行当前过滤器。
  2. 通过AuthenticationManagerResolver获取AuthenticationManager
  3. 通过AuthenticationManger获取Authentication。完成用户认证和获取用户信息
  4. 如果认证失败:通过AuthenticationFailureHandler返回失败信息
  5. 如果认知成功:通过AuthenticationSuccessHandler返回成功信息

类似AuthenticationFilter:功能类
AbstractAuthenticationProcessingFilter
BasicAuthenticationFilter:继承OncePerRequestFilter
DigestAuthenticationFilter:判断请求头:Authorization信息是否存在且以Digest 开头
AbstractPreAuthenticatedProcessingFilter:当前类是通过判断是否存在认证信息,判断是否执行

注意:OncePerRequestFilter,当前过滤器在一个Session只执行一次。

LogoutFilter:退出登录过滤器

  1. 通过SecurityContext获取上下文存储的Authentication信息。
  2. 执行LogoutHandler中的logout完成,退出逻辑处理。
  3. 退出处理完成之后,执行LogoutSuccessHandleronLogoutSuccess方法。完成退出成功后的处理

DefaultLoginPageGeneratingFilter:默认登录页通用过滤器

  1. 通过doFilter完成校验请求链接是否是:loginErrorlogoutSuccessisLoginUrlRequest,跳转到指定链接中。且为GET请求。
  2. loginPageUrl 登录页面URL
  3. logoutSuccessUrl:登录成功URL
  4. failureUrl:登录失败URL
  5. formLoginEnabled: 表单登录是否启动

DefaultLogoutPageGeneratingFilter:默认登录退出通用页面

  1. 判断链接是否是/logout,且为GET请求
  2. 是跳转到退出登录页面

一、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(安全自动配置)

  1. 完成认证异常类,ApplicationEventPublisher
  2. 通过引入类: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安全配置适配器)

  1. ContentNegotiationStrategy:内容协商策略
  2. AuthenticationConfiguration:认证配置
  3. AuthenticationManagerBuilder:认证管理器构建
  4. AuthenticationManager:认证管理
  5. FilterSecurityInterceptor:过滤器安全拦截器
  6. 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则,加载当前配置

  1. AuthenticationManager:认证管理
  2. AuthenticationProvider:认证提供者
  3. 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();//退出登录
  • 作者:swg321321
  • 原文链接:https://blog.csdn.net/swg321321/article/details/126067832
    更新时间:2022年9月29日13:14:42 ,共 12025 字。