Spring Security访问控制Authorization

文章目录

  • Authorization架构
    • 用户权限Authorities
    • 前置调用处理
      • AccessDecisionManager
      • 基于选举机制的AccessDecisionManager
      • RoleVoter
      • AuthenticatedVoter
      • 其它AccessDecisionVoter
    • 后置调用处理
  • HTTP请求访问控制 FilterSecurityInterceptor
  • 表达式访问控制规则
    • 常见的表达式方法
    • Web Security Expressions

Authorization架构

用户权限Authorities

在用户请求通过身份认证后,会在上下文中存储用户信息Authentication,其中Authorities是用户的权限
Spring Security访问控制Authorization_第1张图片
Authorities是GrantedAuthority接口实例的集合(List),GrantedAuthority接口只有一个方法getAuthority(),该方法返回结果类型为String,代表用户的权限。GrantedAuthority由AuthenticationManager管理创建,AccessDecisionManager会读取GrantedAuthority。
在SpringSecurity中只包含了一个GrantedAuthority的具体实现类,SimpleGrantedAuthority,它支持使用字符串表示访问权限。但是在某些业务复杂的场景中,如果字符串无法描述权限时,则需要自己对框架进行扩展。
在这里插入图片描述

前置调用处理

AccessDecisionManager

AccessDecisionManager 接口会被Spring Security的AbstractSecurityInterceptor 调用,来控制访问权限,其包含的方法见以下类图:Spring Security访问控制Authorization_第2张图片

  • decide方法实现了权限判定,传入参数包括Authentication authentication用户信息,Object secureObject被保护的对象,例如一个方法调用,Collection attrs开发人员根据业务进行的权限配置信息。当用户的权限不符合被保护对象的要求时,会抛出AccessDeniedException异常
  • supports(ConfigAttribute attribute): 会在启动时被调用,判断权限配置信息能否支持
  • supports(Class clazz) :判断目标类能否支持

基于选举机制的AccessDecisionManager

Spring Security中包含多种AccessDecisionManager接口的实现,它们都是基于选举机制的。
Spring Security访问控制Authorization_第3张图片
AbstractAccessDecisionManager中聚合了AccessDecisionVoter ,AccessDecisionVoter 用于实现投票选举过程,来判定是否抛出AccessDeniedException 异常
AccessDecisionVoter接口包含三个方法:
Spring Security访问控制Authorization_第4张图片

  • vote:返回int结果,其值有三个ACCESS_GRANTED = 1,ACCESS_ABSTAIN = 0,ACCESS_DENIED = -1,当AccessDecisionVoter不能判断权限时返回ACCESS_ABSTAIN(弃权),否则就要返回ACCESS_GRANTED(授权)或ACCESS_DENIED(拒绝)
  • boolean supports(ConfigAttribute attribute) :功能同AccessDecisionManager
  • boolean supports(Class clazz):功能同AccessDecisionManager

Spring Security中有三种AccessDecisionManager的具体实现,实现了三种不同的投票机制:

  • ConsensusBased:所有投票者通过则授权,所有投票者拒绝则拒绝
  • AffirmativeBased:当有一个投票者同意则授权,所有投票者拒绝才决绝
  • UnanimousBased :当所有投票者同意才授权,有一个投票者拒绝则拒绝
    以上三种实现都提供了参数配置,控制所有投票者都弃权的情况下该如何处理。如果以上都不能满足业务需求,可以自己实现AccessDecisionManager接口进行扩展。

RoleVoter

最常用的AccessDecisionVoter的实现就是RoleVoter,RoleVoter将配置的属性ConfigAttribute视作角色名字,如果用户被分配的相应的角色就会投赞成票。
RoleVoter会在ConfigAttribute 中包含ROLE_开头的属性时进行选举投票,如果用户的角色包含在ConfigAttribute中则投赞成票,在ConfigAttribute没有就投拒绝票,如果ConfigAttribute没有ROLE_开头的属性则投弃权票

AuthenticatedVoter

AuthenticatedVoter是用来区分anonymous, fully-authenticated 和remember-me用户认证的,例如有些网站支持一些特定访问路径支持remember-me认证,其它的路径则需要用户进行登录

其它AccessDecisionVoter

Spring Security访问控制Authorization_第5张图片

AccessDecisionVoter还有其他类型,例如WebExpressionVoter用来处理web授权投票,支持表达式。也可以根据自己的需求自定义AccessDecisionVoter

后置调用处理

尽管AbstractSecurityInterceptor会在执行被保护的对象前调用AccessDecisionManager,但一些应用需要修改被保护对象的返回值,虽然可以通过AOP组件来实现这个目的,但Spring Security也提供了一种便捷的方式。
下图为事后调用处理AfterInvocationManager的类图:
Spring Security访问控制Authorization_第6张图片
接口AfterInvocationManager只有一个实现类AfterInvocationProviderManager,AfterInvocationProviderManager聚合了AfterInvocationProvider的List,AfterInvocationProvider可以修改被保护对象的返回值。

HTTP请求访问控制 FilterSecurityInterceptor

FilterSecurityInterceptor提供HTTPServletRequest的访问控制服务,FilterSecurityInterceptor会被编排进SecurityFilterChain中。
Spring Security访问控制Authorization_第7张图片
(1)FilterSecurityInterceptor从SecurityContextHolder中提取Authentication信息
(2)FilterSecurityInterceptor创建FilterInvocation,FilterInvocation的构造方法参数为HttpServletRequest, HttpServletResponse, FilterChain,即过滤器的入参
(3)将FilterInvocation 传递给SecurityMetadataSource而获取ConfigAttribute
(4)将Authentication, FilterInvocation, ConfigAttributes传递给AccessDecisionManager
(5)如果访问授权为拒绝,会抛出AccessDeniedException异常,ExceptionTranslationFilter会对此异常进行处理
(6)如果访问授权通过,FilterSecurityInterceptor 会继续调用doFilter方法,继续执行过滤链

Spring Scurity默认会对所有请求进行访问控制,其默认的配置类似以下:

protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .authorizeRequests(authorize -> authorize
            .anyRequest().authenticated()
        );
}

也可以根据自身需求进行客户化配置:

protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .authorizeRequests(authorize -> authorize       ①                           
            .mvcMatchers("/resources/**", "/signup", "/about").permitAll().mvcMatchers("/admin/**").hasRole("ADMIN").mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')").anyRequest().denyAll());
}

① authorizeRequests方法可以配置多条访问控制规则
② 任意用户可以访问以"/resources/****“开头, 等于”/signup", 等于"/about"的URL**
③ 以"/admin/**“开头的URL可以由拥有“ROLE_ADMIN”角色的用户访问
④ 以”/db/“开头的URL可以由同时拥有"ROLE_ADMIN” and "ROLE_DBA"的用户访问
⑤ 其他没有赔匹配的URL都不允许访问,这是一个推荐的配置策略,以防用户忘记配置控制规则

表达式访问控制规则

常见的表达式方法

Expression Description
hasRole(String role) 如果当前用户有指定的角色则返回true,例如hasRole(‘admin’)
hasAnyRole(String…​ roles) 如果当前用户拥有指定用户的任意一个则返回true,例如hasAnyRole(‘admin’, ‘user’)
hasAuthority(String authority) 如果当前用户有指定的权限则返回true,例如hasAuthority(‘read’)
hasAnyAuthority(String…​ authorities) 如果当前用户有指定的权限中的任意一个则返回true,例如hasAnyAuthority(‘read’, ‘write’)
principal 获取principal对象,代表当前用户
authentication 从SecurityContext提取Authentication 对象
permitAll 返回true
denyAll 返回false
isAnonymous() 如果当前用户是匿名用户则返回true
isRememberMe() 如果当前用户是remember-me用户
isAuthenticated() 如果当前用户是非匿名用户则返回true

Web Security Expressions

当在访问权限配置中使用表达式时,AccessDecisionManager会调用 WebExpressionVoter来进行控制权限的选举投票。
(1) 在Web Security Expressions中引用bean,使用@beanname

http
    .authorizeRequests(authorize -> authorize
        .antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
        ...
    )

(2) Web Security Expressions Path Variables

http
    .authorizeRequests(authorize -> authorize
        .antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
        ...
    );

你可能感兴趣的:(SpringSecurity)