Files
rikako-note/spring/Spring Security/Spring Security.md
wu xiangkai e805a65039 日常提交
2022-10-08 18:04:38 +08:00

6.8 KiB
Raw Blame History

Spring Security

Spring Security简介

Spring Security作为一个安全框架向使用者提供了用户认证、授权、常规攻击保护等功能。

Spring Security自动配置

默认情况下在引入Spring Security的启动器依赖之后Spring Boot自动配置会做如下工作

  • 启用Spring Security的默认配置创建一个的bean对象bean对象名称为“springSecurityFilterChain”bean对象类型为SecurityFilterChain实现了Filter。该bean对象为负责应用中所有与安全相关的操作例如验证提交的username和password保护应用的url等
  • 创建一个UserDetailsService的bean对象并且产生一个为”user“的username和一个随机产生的password随机产生的password会输出在console日志中
  • 将名为springSecurityFilterChain的bean对象注册到servlet容器中用来对每个servlet请求进行处理

Spring Security会通过Spring Security会通过BCtyptHash解密算法来对密码的存储进行保护

Spring Security结构

DelegatingFilterProxy

Spring提供了Filter的一个实现类DelegatingFilterProxy其将Servlet容器的生命周期和Spring的ApplicationContext连接在一起。
DelegatingFilterProxy会通过标准的servlet容器机制被注册但是将所有工作都委托给Spring容器中实现了Filter的bean对象

DelegatingFilterProxy会在ApplicationContext中查找实现了Filter的bean对象并且调用该bean对象的doFilter方法

public void doFilter(ServletRequest request, 
   ServletResponse response, FilterChain chain) {
  // Lazily get Filter that was registered as a Spring Bean
  // For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
  Filter delegate = getFilterBean(someBeanName);
  // delegate work to the Spring Bean
  delegate.doFilter(request, response);
}

FilterChainProxy

Spring Security支持FilterChainProxyFilterChainProxy是一个由Spring Security提供的特殊FilterFilterChainProxy通过SecurityFilterChain允许向多个Filters委托工作。
FilterChainProxy是一个bean对象通过被包含在DelegatingFilterProxy中。

SecurityFilterChain

SecurityFilterChain通常被FilterChainProxy使用用来决定在该次请求中调用那些Spring Security Filters。

SecurityFilters

Security Filters通过SecurityFilterChain API被插入到FilterChainProxy中。

处理Security异常

ExceptionTranslationFilter将认证异常和权限异常翻译为http response。

ExceptionTranslationFilter被插入到FilterChainProxy中作为SecurityFilterChain中的一个。
如果应用程序没有抛出AccessDeniedException或AuthenticationException那么ExceptionTranslationFilter并不会做任何事情。

// ExceptionTranslationFilter的伪代码
try {
	filterChain.doFilter(request, response); 
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		startAuthentication(); 
	} else {
		accessDenied(); 
	}
}

Spring Security身份认证的结构

SecurityContextHolder

Spring Security身份认证的核心模型SecurityContextHolder包含有SecurityContext。

Spring Security将被认证用户的详细信息details存储在SecurityContextHolder中。Spring Security不在乎该SecurityContextHolder是如何被填充的只要该SecurityContextHolder有值那么其将会被用作当前已经被认证的用户。

将用户标识为已认证的最简单的方法是为该用户设置SecurityContextHolder。

// 设置SecurityContextHolder
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
    new TestingAuthenticationToken("username", "password", "ROLE_USER");
context.setAuthentication(authentication);

SecurityContextHolder.setContext(context);

默认情况下SecurityContextHolder通过ThreadLocal来存储SecurityContext故而SecurityContext对于位于同一线程之下的方法来说都可以访问。

使用ThreadLocal来存储SecurityContext是相当安全的如果想要在该已认证主体的请求被处理完成之后清除SecurityContextSpring Security中的FilterChainProxy会保证该SecurityContext被清除。

SecurityContext

SecurityContext从SecurityContextHolder中获得SecurityContext中含有Authentication对象。

Authentication

Authentication在Spring Security中具有两个目的

  • 作为AuthenticationManager的输入用于提供待认证用户的认证凭证。当用于该场景下时isAuthenticated()方法返回值应该为false
  • 代表当前已经认证过的用户。当前的Authentication可以从SecurityContext中获取而默认情况下SecurityContext是存储在ThreadLocal中的

Authentication含有如下部分

  • 主体principal用于标识用户当通过username/password进行认证时其通常是UserDetails类的实例
  • 凭据credentials通常是password在许多场景下凭据会在用户认证成功之后被清空为了保证凭据不会被泄露
  • 权限authorities该GrantedAuthority集合是用户被授予的高层次许可。许可通常是用户角色或者作用域范围。

GrantedAuthority

GrantedAuthority是用户被授予的高层次许可譬如用户角色或者作用域范围。
GrantedAuthority可以通过Authentication.getAuthorities()方法来获得该方法会返回一个GrantedAuthentication的集合。每个GrantedAuthentication都是一项被授予该用户的权限。

AuthenticationManager

AuthenticationManager的API定义了Security Filters如何来执行身份认证。对于身份认证返回的Authentication会被调用AuthenticationManager的controller设置到SecurityContextHolder中。

AuthenticationManager的实现可以是任何类但是最通用的实现仍然是ProviderManager

ProviderManager

ProviderManager是AuthenticationManager的最通用实现。ProviderManager将工作委托给一系列AuthenticationProvider。

对于每个ProviderManager都可以决定将该认证标识为成功、失败或者将认证工作委托给下游AuthenticationProvider。 如果所有的AuthenticationProvider都没有将该认证标识为成功或者失败那么整个认证流程失败并且抛出ProviderNotFoundException异常。
ProviderNotFoundException是一个特殊的AuthenticationException该异常代表对传入Authentication的类型并没有配置该类型的ProviderManager

在实践中每个AuthenticationProvider知道如何处理一种特定类型的Authentication

默认情况下ProviderManager在认证请求成功后会尝试清除返回的Authentication对象中任何敏感的凭据信息这将会保证password等敏感信息保存时间尽可能地短减少泄露的风险。