1.Spring security架构概览

Spring security架构概览

1.2Spring security核心功能

对于一个安全管理框架而言,无论是shiro还是spring security,最核心的功能,无非就是如下两方面:

  • 认证,即身份验证(你是谁)。
  • 授权,即访问控制(你可以做什么)。
1.3Spring security整体架构
1.3.1认证和授权
1.3.1.1认证

在spring security的架构设计中,认证和授权是分开的。用户的认证信息主要由Authentication的实现类来保存:

/**
 * 在AuthenticationManager.authenticate(Authentication)方法处理完请求后,表示身份验证请求或经过身份验证的主体的令牌
 */
public interface Authentication extends Principal, Serializable {
    // 获取用户的权限
    Collection<? extends GrantedAuthority> getAuthorities();

    // 获取用户凭证,一般来说就是密码
    Object getCredentials();

    // 获取用户携带的详细信息,可能是当前请求之类等
    Object getDetails();

    // 获取当前用户,例如是一个用户名或者一个用户对象
    Object getPrincipal();

    // 当前用户是否认证成功
    boolean isAuthenticated();
	
	// 实现应该始终允许使用false参数调用该方法,因为不同的类使用该参数来指定不应该信任的身份验证令牌。
	// 如果实现希望拒绝带有true参数的方法调用(这将表明身份验证令牌是可信的——一个潜在的安全风险),则应该抛出一个IllegalArgumentException。
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

当用户使用用户名/密码登录或使用remember-me登录时,都会对应一个Authentication实例。
Spring security中的认证工作主要由AuthenticationManager接口来负责:

/**
 * 处理Authentication请求
 */
public interface AuthenticationManager {
    /**
     * 该方法有三个不同的返回值:
     * 1. 返回Authentication,表示认证成功。
     * 2. 抛出AuthenticationException异常,表示用户输入了无效的凭证。
     * 3. 返回null,表示不能断定。
     */
    Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

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

/**
 * 表明类可以处理特定的Authentication实现
 */
public interface AuthenticationProvider {
    Authentication authenticate(Authentication authentication) throws AuthenticationException;

    boolean supports(Class<?> authentication);
}

由于Authentication拥有众多不同的实现类,这些不同的实现类又由不同的AuthenticationProvider来处理,所有AuthenticationProvider会有一个supports方法,用来判断当前的AuthenticationProvider是否支持对应的Authentication
在一次完整的认证流程中,可能会同时存在多个AuthenticationProvider(例如,项目同时支持表单登录和短信验证码登录),多个AuthenticationProvider统一由ProviderManager来管理。同时,ProviderManager具有一个可选的parent,如果所有的AuthenticationProvider都认证失败,那么就会调用parent进行认证。parent相当于一个备用的认证方式,即各个AuthenticationProvider都无法处理认证问题的时候,就由parent出场收拾残局。

1.3.1.2授权

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

  • AccessDecisionManager
  • AccessDecisionVoter

AccessDecisionVoter是一个投票器,投票器会检查用户是否具备应有的角色,进而投出赞成、反对或者弃权票;AccessDecisionManager则是一个决策器,来决定此次访问是否被允许。AccessDecisionVoterAccessDecisionManager都有众多的实现类,在AccessDecisionManager中会挨个遍历AccessDecisionVoter,进而决定是否允许用户访问,因而AccessDecisionVoterAccessDecisionManager两者的关系类似于AuthenticationProviderProviderManager的关系。
在spring security中,用户请求一个资源(通常是一个网络接口或者一个java方法),所需要的角色会被封装成一个ConfigAttribute对象,在ConfigAttribute中只有一个getAttribute方法,该方法返回一个字符串,就是角色的名称。一般来说,角色名称都带有一个ROLE_前缀,投票器AccessDecisionVoter所做的事情,其实就是比较用户所具备的角色和请求某个资源所需的ConfigAttribute之间的关系。

1.3.2Web安全

在spring security中,认证、授权等功能都是基于过滤器来完成的。这些过滤器按照既定的优先级排列,最终形成一个过滤器链。开发者也可以自定义过滤器,并通过@Order注解去调整自定义过滤器在过滤器链中的位置。
需要注意的是,默认过滤器并不是直接放在web项目的原生过滤器链中,而是通过一个FilterChainProxy来统一管理。Spring security中的过滤器链通过FilterChainProxy嵌入到web项目的原生过滤器链中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IPrQ8Feq-1642578298570)(en-resource://database/720:1)]@w=500

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IuJzoduv-1642578298571)(en-resource://database/721:1)]@w=500

FilterChainProxy作为一个顶层管理者,将统一管理security filter。FilterChainProxy本身将通过spring框架提供的DelegatingFilterProxy整合到原生过滤器链中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N8E13YYb-1642578298571)(en-resource://database/722:1)]@w=500

1.3.3登录数据保存

如果不使用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 ApplicationConfiguration extends AsyncConfigurerSupport {
    @Override
    public Executor getAsyncExecutor() {
        return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值