Spring Security整体架构

Spring Security整体架构

1.认证

​ 在Spring Security的架构设计中,认证AuthenticationAuthorization是分开的,无论使用什么样的认证方式,都不会影响授权,这时两个独立的存在。这种独立的存在带来的好处之一,就是Spring Security可以非常方便地整合一些外部的认证方案。

​ 在Spring Security中,用户的认证信息主要由Authentication的实现类来保存,其中Authentication接口定义如下:

public interface Authentication extends Principal, Serializable {
    
    Collection<? extends GrantedAuthority> getAuthorities();

    Object getCredentials();

    Object getDetails();

    Object getPrincipal();

    boolean isAuthenticated();

    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
    
}

Authentication接口中定义的方法如下:

  • getAuthorities():获取用户的权限。
  • getCredentials():获取用户凭证,也就是密码。
  • getDetails():获取用户携带的详细信息。
  • getPrincipal():获取当前用户,例如一个用户名或者一个用户对象。
  • isAuthenticated():当前用户是否认证成功。

​ 当用户使用用户名/密码登录或使用RememberMe登录时,都会对应一个不同的Authentication实例。

Spring Security中的认证工作主要由AuthenticationManager接口负责,AuthenticationManager接口定义如下:

public interface AuthenticationManager {
    
    Authentication authenticate(Authentication authentication) throws AuthenticationException;
    
}

AuthenticationManager接口中只有一个authenticate方法可以用来做认证,该方法有三个不同的返回值:

  • 返回Authentication,表示认证成功。
  • 抛出AuthenticationException异常,表示用户输入了无效的凭证。
  • 返回null,表示不能断定。

AuthenticationManager接口最主要的实现类是ProviderManagerProviderManager类管理了众多的AuthenticationProvider实例。AuthenticationProvider类有点类似于AuthenticationManager,但是它多了一个supports方法用来判断是否支持给定的Authentication类型。AuthenticationProvider接口定义如下:

public interface AuthenticationProvider {
    
    Authentication authenticate(Authentication authentication) throws AuthenticationException;

    boolean supports(Class<?> clazz);
    
}

​ 由于Authentication接口拥有众多不同的实现类,这些不同的实现类又由不同的AuthenticationProvider来处理,所以AuthenticationProvider会有一个supports方法,用来判断当前的AuthenticationProvider是否支持对应的Authentication

​ 在一次完整的认证流程中,可能会同时存在多个AuthenticationProvider,例如项目同时支持form表单登录和短信验证码登录。多个AuthenticationProvider统一由ProviderManager来管理。同时,ProviderManager具有一个可选的parent,如果所有的AuthenticationProvider都认证失败,那就会调用parent进行认证。parent相当于一个具备认证方式,但当各个AuthenticationProvider都无法处理认证问题的时候,就由parent出面收拾残局。


2.授权

​ 当完成认证后,接下来就是授权了。在Spring Security的授权体系中,有两个关键的接口:

  • AccessDecisionManager
  • AccessDecisionVoter

AccessDecisionVoter是一个投票器,投票器会检查用户是否具备应有的校色,进而投出赞成、反对或者弃权票。AccessDecisionManager则是一个决策器,来决定此次访问是否被允许。AccessDecisionManagerAccessDecisionVoter都有众多的实现类。在AccessDecisionManager中会挨个遍历AccessDecisionVoter,进而决定是否允许用户访问,因此AccessDecisionVoterAccessDecisionManager两者的关系类似于AuthenticationProviderProviderManager的关系。

Spring Security中,用户请求一个资源,这个资源通常是一个网络接口或者一个Java方法所需要的角色会被封装成一个ConfigAttribute对象,在ConfigAttribute中只有一个getAttribute方法,该方法返回一个String字符串,就是角色的名称。一般来说,一个角色名称都带有一个ROLE_前缀,投票器AccessDecisionVoter所作的事情,其实就是比较用户所具备的角色和请求某个资源所需的ConfigAttribute之间的关系。ConfigAttribute接口定义如下:

public interface ConfigAttribute extends Serializable {
    
    String getAttribute();
    
}

3.Web安全

​ 在Spring Security中,认证、授权等功能都是基于过滤器来完成的。下列为Spring Security中常见的过滤器。

过滤器过滤器作用是否默认加载
ChannelProcessingFilter过滤请求协议,如HTTPHTTPSNO
WebAsyncManagerIntegrationFilterWebAsyncManagerSpring Security上下文进行集成YES
SecurityContextPersistenceFilter在处理请求之前,将安全信息加载到SecurityContextHolder中以便后续使用。请求结束后再擦除SecurityContextHolder中的信息YES
HeaderWriterFilter头信息加入到响应中YES
CorsFilter处理跨域请求问题NO
CsrfFilter处理CSRF攻击YES
LogoutFilter处理注销登录YES
OAuth2AuthorizationRequestRedirectFilter处理OAuth2认证重定向NO
Saml2WebSsoAuthenticationRequestFilter处理SAML认证NO
X509AuthenticationFilter处理X509认证NO
AbstractPreAuthenticatedProcessingFilter处理预认证问题NO
CasAuthenticationFilter处理CAS单点登录NO
OAuth2LoginAuthenticationFilter处理OAuth2认证NO
Saml2WebSsoAuthenticationFilter处理SAML认证NO
UsernamePasswordAuthenticationFilter处理表单登录YES
OpenIDAuthenticationFilter处理OpenID认证NO
DefaultLoginPageGeneratingFilter配置默认登录页面YES
DefaultLogoutPageGeneratingFilter配置默认注销页面YES
ConcurrentSessionFilter处理session有效期NO
DigestAuthenticationFilter处理HTTP摘要认证NO
BearerTokenAuthenticationFilter处理OAuth2认证时的Access TokenNO
BasicAuthenticationFilter处理HttpBasic登录YES
RequestCacheAwareFilter处理请求缓存YES
SecurityContextHolderAwareRequestFilter包装原始请求YES
JaasApiIntegrationFilter处理JAAS认证NO
RememberMeAuthenticationFilter处理RememberMe登录NO
AnonymousAuthenticationFilter配置匿名认证YES
OAuth2AuthorizationCodeGrantFilter处理OAuth2认证中的授权码NO
SessionManagementFilter处理Session并发问题YES
ExceptionTranslationFilter处理异常/授权中的情况YES
FilterSecurityInterceptor处理授权YES
SwitchUserFilter处理账号切换NO

以上过滤器是否默认加载是指引入Spring Security依赖之后,开发者不做任何配置时,会自动加载的过滤器。

​ 开发者所见到的Spring Security提供的功能,都是通过这些过滤器实现的,这些过滤器按照既定的优先级排列,最终形成一个过滤器链。开发者也可以自定义过滤器,并通过@Order注解去调整自定义过滤器在过滤器链中的位置。

​ 需要注意的是,默认过滤器并不是直接放在Web项目的原生过滤器中,而是通过一个FilterChainProxy来统一管理。Spring Security中的过滤链通过FilterChainProxy嵌入到Web项目的原生过滤器链中。

FilterChainProxy

​ 在Spring Security中,这样的过滤器链不仅仅只有一个,可能会有多个。当存在多个过滤器链时,多个过滤器链之间要指定优先级,当请求到达后,会从FilterChainProxy进行分发,先和哪个过滤器链匹配上,就用哪个过滤器链进行处理。当系统中存在多个不同认证体系时,使用多个过滤器链就非常有效。

SecurityFilterChain

FilterChainProxy作为一个顶层管理者,将统一管理Security FilterFilterChainProxy本身将通过Spring框架提供的DelegatingFilterProxy整合到原生过滤器链中,所以上图还可以做进一步优化:

DelegatingFilterProxy


4.登录数据保存

​ 如果不使用Spring Security这一类安全管理框架,大部分的开发者可能会将登录用户数据保存在session中。事实上,Spring Security也是这么做的。但是,为了使用方便,Spring Security在此基础上还做了一些改进,其中最重要的一个变化就是线程绑定。

​ 当用户登陆成功后,Spring Security会将登陆成功的用户信息保存到SecurityContextHolder中。SecurityContextHolder中的数据保存默认是通过ThreadLocal来实现的。使用ThreadLocal创建的变量只能被当前线程访问,不能被其它线程访问和修改,也就是将用户数据与请求线程绑定在一起。当请求处理完毕后,Spring Security会将SecurityContextHolder中的数据拿出来保存到Session中,同时将SecurityContextHolder中的数据清空。每当有请求到来时,Spring Security会先从Session中取出用户登录数据,保存到SecurityContextHolder中,方便在该请求的后续处理过程中使用。同时在请求结束时将SecurityContextHolder中的数据拿出来保存到Session中,然后将SecurityContextHolder中的用户数据清空。

​ 这一策略非常方便用户在Controller或者Service层获取当前登录用户数据,但是带来的另外一个问题就是,在字线程中想要获取用户登录数据就比较麻烦。Spring Security对此也提供了相应的解决方案,如果开发者使用@Async注解来开启异步任务的话,那么只需要添加如下配置,使用Spring Security提供的异步任务代理,就可以在异步任务中从SecurityContextHolder中获取当前登录用户的信息:

@Configuration
public class ApplicationAsyncConfiguration extends AsyncConfigurerSupport {

    @Override
    public Executor getAsyncExecutor() {
        return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值