核心概念
为了方便大家理解后面的内容,我们先对 Spring Security 中一些权限相关的核心概念做个简单介绍。
角色与权限
根据前面 1.3.1 小节的介绍,大家已经知道在 Spring Security 中,当用户登录成功后,当前登录用户信息将保存在 Authentication 对象中,Authentication 对象中有一个 getAuthorities 方法,用来返回当前对象所具备的权限信息,也就是已经授予当前登录用户的权限。getAuthorities 方法返回值是 Collection<?extends GrantedAuthority>,即集合中存放的是 GrantedAuthority 的子类,当需要进行权限判断的时候,就会调用该方法获取用户的权限,进而做出判断。
无论用户的认证方式是用户名/密码形式、RememberMe 形式,还是其他如 CAS、OAuth2 等认证方式,最终用户的权限信息都可以通过 getAuthorities 方法获取。这就是我们前面所讲的,无论用户采用何种认证方式,都不影响授权。
那么对于 Authentication#getAuthorities 方法的返回值,我们应该理解为用户的角色还是用户的权限呢?
从设计层面来讲,角色和权限是两个完全不同的东西:权限就是一些具体的操作,例如针对员工数据的读权限(READ_EMPLOYEE)和针对员工数据的写权限(WRITE_EMPLOYEE);角色则是某些权限的集合,例如管理员角色 ROLE_ADMIN、普通用户角色 ROLE_USER。
从代码层面来讲,角色和权限并没有太大的不同,特别是在 Spring Security 中,角色和权限的处理的方式基本上是一样的,唯一的区别在于 Spring Security 在多个地方会自动给角色添加一个 ROLE_ 前缀,而权限则不会自动添加任何前缀。这里读者需要特别注意,我们在后面的讲解中,遇到自动添加 ROLE_ 前缀的地方也都会和大家重点说明。
至于 Authentication#getAuthorities 方法的返回值,则要分情况来对待:
-
如果权限系统设计比较简单,就是用户
<=>
权限<=>
资源三者之简的关系,那么 getAuthorities 方法的含义就很明确,就是返回用户的权限。 -
如果权限系统设计比较复杂,同时存在角色和权限的概念,如用户
<=>
角色<=>
权限<=>
资源(用户关联角色、角色关联权限、权限关联资源),此时我们可以将 getAuthorities 方法的返回值当作权限来理解。由于 Spring Security 并未提供相关的角色类,因此这个时候需要我们自定义角色类。
对于第一种情况,大家都好理解,这里不再赘述。对于第二种情况,我们简单介绍一下。
如果系统同时存在角色和权限,我们可以使用 GrantedAuthority 的实现类 SimpleGrantedAuthority 来表示一个权限,在 SimpleGrantedAuthority 类中,我们可以将权限描述为一个字符串,如 READ_EMPLOYEE、WRITE_EMPLOYEE。据此,我们定义角色类如下:
public class Role implements GrantedAuthority {
private String name;
private List<SimpleGrantedAuthority> allowedOperations = new ArrayList<>();
@Override
public String getAuthority() {
return name;
}
// 省略 getter/setter
}
角色继承自 GrantedAuthority,一个角色对应多个权限。然后在定义用户类的时候,将角色转为权限即可:
public class User implements UserDetails {
private List<Role> roles = new ArrayList<>();
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.addAll(role.getAllowedOperations());
}
// 省略 getter/setter
}
}
整体上来说,设计层面上,角色和权限是两个东西:代码层面上,角色和权限其实差别不大,注意区分即可。
角色继承
角色继迷承就是指角色存在一个上下级的关系,例如 ROLE_ADMIN 继承自 ROLE_USER,那么 ROLE_ADMIN 就自动具备 ROLE_USER 的所有权限。
Spring Security 中通过 RoleHierarchy 类对角色继承提供支持,我们来看一下它的源码:
public interface RoleHierarchy {
Collection<? extends GrantedAuthority> getReachableGrantedAuthorities(
Collection<? extends GrantedAuthority> authorities);
}
RoleHierarchy 中只有一个 getReachableGrantedAuthorities 方法,该方法返回用户真正 “可触达” 的权限。举个简单例子,假设用户定义了 ROLE_ADMIN 继承自 ROLE_USER,ROLE_USER 继承自 ROLE_GUEST,现在当前用户角色是 ROLE_ADMIN,但是它实际可访问的资源也包含 ROLE_USER 和 ROLE_GUEST 能访问的资源。getReachableGrantedAuthorities 方法就是根据当前用户所具有的角色,从角色层级映射中解析出用户真正 “可触达” 的权限。
RoleHierarchy 只有一个实现类 RoleHierarchyImpl,开发者一般通过 RoleHierarchyImpl 类来定义角色的层级关系,如下面代码表示 ROLE_C 继承自ROLE_D,ROLE_B 继承自 ROLE_C,ROLE_A 继承自 ROLE_B:
@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_A > ROLE_B > ROLE_C > ROLE_D");
return hierarchy;
}
这样的角色层级,在 RoleHierarchyImpl 类中首先通过 buildRolesReachableInOneStepMap 方法解析成下面这样的 Map 集合(key->value
):
ROLE A->ROLE B
ROLE B->ROLE C
ROLE C->ROLE D
然后再通过 buildRolesReachableInOneOrMoreStepsMap 方法对上面的集合再次解析,最终解析结果如下:
ROLE A->[ROLE_B,ROLE_C,ROLE_D]
ROLE B->[ROLE_C,ROLE_D]
ROLE C->ROLE_D
最后通过 getReachableGrantedAuthorities 方法从该 Map 集合中获取用户真正 “可触达” 的权限。
两种处理器
前面我们讲了,Spring Security 中提供的权限管理功能主要有两种类型:基于过滤器的权限管理(FilterSecurityInterceptor)和基于 AOP 的权限管理(MethodSecurityInterceptor)。
无论是哪种,都涉及一个前置处理器和后置处理器。图13-1和图13-2所示分别表示基于过滤器的权限管理和基于 AOP 的权限管理中的前置处理器和后置处理器。


在基于过滤器的权限管理中,请求首先到达过滤器 FilterSecurityInterceptor,在其执行过程中,首先会由前置处理器去判断发起当前请求的用户是否具备相应的权限,如果具备,则请求继续向下走,到达目标方法并执行完毕。在响应时,又会经过 FilterSecurityInterceptor 过滤器,此时由后置处理器再去完成其他收尾工作。在基于过滤器的权限管理中,后置处理器一般是不工作的。这也很好理解,因为基于过滤器的权限管理,实际上就是拦截请求 URL 地址,这种权限管理方式粒度较粗,而且过滤器中拿到的是响应的 HttpServletResponse 对象,对其所返的数据做二次处理并不方便。
在基于方法的权限管理中,目标方法的调用会被 MethodSecurityInterceptor 拦截下来,实现原理当然就是大家所熟知的 AOP 机制。当目标方法的调用被 MethodSecurityInterceptor 拦截下之后,在其 invoke 方法中首先会由前置处理器去判断当前用户是否具备调用目标方法所需要的权限,如果具备,则继续执行目标方法。当目标方法执行完毕并给返回结果后,在 MethodSecurityInterceptor#invoke 方法中,由后置处理器再去对目标方法的返回结果进行过滤或者鉴权,然后在 invoke 方法中将处理后的结果返回。
可以看到,无论是基于过滤器的权限管理还是基于方法的权限管理,前置处理器者都是重中之重,两者都会用到。而后置处理器则只是在基于方法的权限管理中会用到。
前置处理器
要理解前置处理器,需要先了解投票器。
投票器
投票器是 Spring Security 权限管理功能中的一个组件,顾名思义,投票器的作用就是针对是否允许某一个操作进行投票。当请求的 URL 地址被拦截下来之后,或者当调用的方法被 AOP 截下来之后,都会调用投票器对当前操作进行投票,以便决定是否允许当前操作。
在 Spring Security 中,投票器由 AccessDecisionVoter 来定义,我们来看一下这个接口:
public interface AccessDecisionVoter<S> {
int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = -1;
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
int vote(Authentication authentication, S object,
Collection<ConfigAttribute> attributes);
}
-
这个接口首先定义了三个常量:ACCESS_GRANTED 表示投票通过、ACCESS_ABSTAIN 表示弃权、ACCESS_DENIED 表示拒绝。
-
supports(ConfigAttribute) 方法:用来判断是否支持处理 ConfigAttribute 对象。
-
supports(Class) 方法:用来判断是否支持处理受保护的安全对象。
-
vote 方法:则是具体的投票方法,根据用户所具有的权限以及当前请求需要的权限进行投票。vote 方法有三个参数:第一个参数 authentication 中可以提取出来当前用户所具备的权限。第二个参数 object 表示受保护的安全对象,如果受保护的是 URL 地址,则 object 就是一个 FilterInvocation 对象;如果受保护的是一个方法,则 object 就是一个 MethodInvocation 对象。第三个参数 attributes 表示访问受保护对象所需要的权限。vote 方法的返回值就是前面所定义的三个常量之一。
Spring Security 中为 AccessDecisionVoter 提供了诸多不同的实现类,图 13-3 所示是 AccessDecisionVoter 的所有继承类。

-
RoleVoter:RoleVoter 是根据登录主体的角色进行投票,即判断当前用户是否具备受保护对象所需要的角色。需要注意的是,默认情况下角色需以 “ROLE_” 开始,否则 suppots 方法直接返回 false,不进行后续的投票操作。
-
RoleHierarchyVoter:RoleHierarchyVoter 继承自 RoleVoter,投票逻辑和 RoleVoter 一致,不同的是 RoleHierarchyVoter 支持角色的继承,它通过 RoleHierarchyImpl 对象对用户所具有的角色进行解析,获取用户真正 “可触达” 的角色;而 RoleVoter 则直接调用 authentication.getAuthorities() 方法获取用户的角色。
-
WebExpressionVoter:基于 URL 地址进行权限控制时的投票器(支持 SpEL)。
-
Jsr250Voter:处理 JSR-250 权限注解的投票器,如 @PermitAll、@DenyAll 等。
-
AuthenticatedVoter:AuthenticatedVoter 用于判断当前用户的认证形式,它有三种取值: IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED 以及 IS_AUTHENTICATED_ANONYMOUSLY。其中: IS_AUTHENTICATED_FULLY 要求当前用户既不是匿名用户也不是通过 RememberMe 进行认证;IS_AUTHENTICATED_REMEMBERED 则在前者的基础上,允许用户通过 RememberMe 进行认证;IS_AUTHENTICATED_ANONYMOUSLY 则允许当前用户通过 RememberMe 认证,也允许当前用户是匿名用户。
-
AbstractAclVoter:基于 ACL 进行权限控制时的投票器。这是一个抽象类,没有绑定到具体的 ACL 系统(关于 ACL,本书第 14 章会做详细介绍)
-
AclEntryVoter:AclEntryVoter 继承自 AbstractAclVoter,基于 Spring Security 提供的 ACL 权限系统的投票器。
-
PreInvocationAuthorizationAdviceVoter:处理 @PreFilter 和 @PreAuthorize 注解的投票器。
这就是 Spring Security 中提供的所有投票器,在具体使用中,可以单独使用一个,也可以多个一起使用。如果上面这些投票器都无法满足需求,开发者也可以自定义投票器。需要注意的是,投票结果并非最终结果(通过或拒绝),最终结果还要看决策器(AccessDecisionManager)。
决策器
决策器由 AccessDecisionManager 负责,AccessDecisionManager 会同时管理多个投票器,由 AccessDecisionManager 调用投票器进行投票,然后根据投票结果做出相应的决策,所以我们将 AccessDecisionManager 也称作是一个决策管理器。我们来看一下它的源码:
public interface AccessDecisionManager {
void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
InsufficientAuthenticationException;
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
}
这里一共有三个方法:
-
decide 方法:是核心的决策方法,在这个方法中判断是否允许当前 URL 或者方法的调用,如果不允许,则会抛出 AccessDeniedException 异常。
-
supports(ConfigAttribute)方法:用来判断是否支持处理 ConfigAttribute 对象。
-
supports(Class) 方法:用来判断是否支持当前安全对象。
我们来看一幅官方提供的类图,如图13-4所示。

从图中可以看出,AccessDecisionManager有一个实现类 AbstractAccessDecisionManager,一个 AbstractAccessDecisionManager 对应多个投票器。多个投票器针对同一个请求可能会给出不同的结果,那么听谁的呢?这就要看决策器了。
-
AffirmativeBased:一票通过机制,即只要有一个投票器通过就可以访问(默认即此)。
-
UnanimousBased:一票否决机制,即只要有一个投票器反对就不可以访问。
-
ConsensusBased:少数服从多数机制。如果是平局并且至少有一张赞同票,则根据 allowIfEqualGrantedDeniedDecisions 参数的取值来决定,如果该参数取值为 true,则可以访问,否则不可以访问。
这是 Spring Security 中提供的三个决策器,如果这三个决策器无法满足需求,开发者也可以自定义类继承自 AbstractAccessDecisionManager 实现自己的决策器。
这就是前置处理器中的大致逻辑,无论是基于 URL 地址的权限管理,还是基于方法的权限管理,都是在前置处理器中通过 AccessDecisionManager 调用 AccessDecisionVoter 进行投票,进而做出相应的决策。
后置处理器
后置处理器一般只在基于方法的权限控制中会用到,当目标方法执行完毕后,通过后置处理器可以对目标方法的返回值进行权限校验或者过滤。
后置处理器由 AfterInvocationManager 负责,我们来看一下它的源码:
public interface AfterInvocationManager {
Object decide(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes, Object returnedObject)
throws AccessDeniedException;
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
}
AfterInvocationManager 和 AccessDecisionManager 的源码高度相似,主要的区别在于 decide 方法的参数和返回值。当后置处理器执行时,被权限保护的方法已经执行完毕,后置处理器主要是对执行的结果进行过滤,所以 decide 方法中有一个 returnedObject 参数,这就是目标方法的执行结果,decide 方法的返回值就是对 returnedObject 对象进行过滤/鉴权后的结果。
我们来看官方提供的一幅 AfterInvocationManager 类图,如图13-5 所示。
有没有感觉眼熟?这就是 AuthenticationManager、ProviderManager 以及 AuthenticationProvider 的翻版。
AfterInvocationManager 只有一个实现类 AfterInvocationProviderManager,一个 AfterInvocationProviderManager 关联多个 AfterInvocationProvider。在 AfterInvocationManager 的 decide 以及 supports 方法执行时,都是遍历 AfterInvocationProvider 并执行它里边对应的方法。

AfterInvocationProvider 有多个不同的实现类,常见到的是 PostInvocationAdviceProvider,该类主要用来处理 @PostAuthorize 和 @PostFilter 注解配置的过滤器。另外还有 AbstractAclProvider 主要用来处理 ACL 中的验证逻辑,它有两个子类,AclEntryAfterInvocationProvider 用来进行权限校验,AclEntryAfterInvocationCollectionFilteringProvider 则用来做集合/数组过滤(官方未给出该类的类图,读者可以查看源码)。
这就是 Spring Security 中提供的后置处理器。
权限元数据
这一小节主要介绍 ConfigAttribute 和 SecurityMetadataSource。
ConfigAttribute
在 13.3.4 小节中介绍投票器时,在具体的投票方法 vote 中,受保护对象所需要的权限保存在一个 Collection<ConfigAttribute> 集合中,集合中的对象是 ConfigAttribute,而不是我们所熟知的 GrantedAuthority,那么这里就需要和大家介绍一下 ConfigAttribute。
ConfigAttribute 用来存储与安全系统相关的配置属性,也就是系统关于权限的配置,通过 ConfigAttribute 来存储,我们来看一下 ConfigAttribute 接口,代码如下:
public interface ConfigAttribute extends Serializable {
String getAttribute();
}
该接口只有一个 getAttribute 方法返回具体的权限字符串,而 GrantedAuthority 中则是通过 getAuthority 方法返回用户所具有的权限,两者返回值都是字符串。所以虽然是 ConfigAttribute 和 GrantedAuthority 两个不同的对象,但是最终是可以比较的。
图13-6所示表示 ConfigAttribute 的所有继承类。

接口就不做过多介绍了,主要来说一下最终的五个比较关键的实现类(接下来涉及的一些注解,会在本章后面做详细介绍):
-
WebExpressionConfigAttribute:如果用户是基于 URL 地址来控制权限并且支持 SpEL,那么默认配置的权限控制表达式最终会被封装为 WebExpressionConfigAttribute 对象。
-
SecurityConfig:如果用户使用了 @Secured 注解来控制权限,那么配置的权限就会被封装为 SecurityConfig 对象。
-
Jsr250SecurityConfig:如果用户使用了 JSR-250 相关的注解来控制权限(如 @PermitAll、@DenyAll),那么配置的权限就会被封装为 Jsr250SecurityConfig 对象。
-
PreInvocationExpressionAttribute:如果用户使用了 @PreAuthorize、@PreFilter 注解来控制权限,那么相关的配置就会被封装为 PreInvocationExpressionAttribute 对象。
-
PostInvocationExpressionAttribute:如果用户使用了 @PostAuthorize、@PostFilter 注解来控制权限,那么相关的配置就会被封装为 PostInvocationExpressionAttribute 对象。
可以看到,针对不同的配置方式,配置数据会以不同的 ConfigAttribute 对象存储。
SecurityMetadataSource
当投票器在投票时,需要两方面的权限:其一是当前用户具备哪些权限; 其二是当前访问的 URL 或者方法需要哪些权限才能访问。投票器所做的事情就是对这两种权限进行比较。
根据 13.3.1 小节的介绍,用户具备的权限保存在 authentication 中,那么当前访问的 URL 或者方法所需要的权限如何获取呢?这就和 SecurityMetadataSource 有关了。
从字面上来理解,SecurityMetadataSource 就是安全元数据源,SecurityMetadataSource 所做的事情,就是提供受保护对象所需要的权限。例如,用户访问了一个 URL 地址,该 URL 地址需要哪些权限才能访问?这个就由 SecurityMetadataSource 来提供。
SecurityMetadataSource 本身只是一个接口,我们来看一下它的源码:
public interface SecurityMetadataSource extends AopInfrastructureBean {
Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException;
Collection<ConfigAttribute> getAllConfigAttributes();
boolean supports(Class<?> clazz);
}
这里只有三个方法:
-
getAttributes:根据传入的安全对象参数返回其所需要的权限。如果受保护的对象是一个 URL 地址,那么传入的参数 object 就是一个 FilterInvocation 对象;如果受保护的是一个方法,那么传入的参数 object 就是一个 MethodInvocation 对象。
-
getAllConfigAttributes:getAllConfigAttributes 方法返回所有的角色/权限,以便验证是否支持。不过这个方法并不是必需的,也可以直接返回 null。
-
supports:返回当前的 SecurityMetadataSource 是否支持受保护的对象如 FilterInvocation 或者 MethodInvocation。
图13-7表示 SecurityMetadataSource 的继承关系。

由图中可以看到,直接继承自 SecurityMetadataSource 的接口主要有两个:FilterInvocationSecurityMetadataSource 和 MethodSecurityMetadataSource。
-
FilterInvocationSecurityMetadataSource:这是一个空接口,更像是一个标记。如果被保护的对象是一个 URL 地址,那么将由 FilterInvocationSecurityMetadataSource 的实现类提供访问该 URL 地址所需要的权限。
-
MethodSecurityMetadataSource:这也是一个接口,t如果受保护的对象是一个方法,那么将通过 MethodSecurityMetadataSource 的实现类来获取受保护对象所需要的权限。
FilterInvocationSecurityMetadataSource 有一个子类 DefaultFilterInvocationSecurityMetadataSource,该类中定义了一个如下格式的 Map 集合:
private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
可以看到,在这个 Map 集合中,key 是一个请求匹配器,value 则是一个权限集合,也就是说 requestMap 中保存了请求 URL 和其所需权限之间的映射关系。在 Spring Security 中,如果直接在 configure(HttpSecurity) 方法中配置 URL 请求地址拦截,像下面这样:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").access("hasRole('user')")
.anyRequest().access("isAuthenticated()")
}
这段配置表示访问 /admin/**
格式的 URL 地址需要 admin 角色,访问 /user/**
格式的 URL 地址需要 user 角色,其余地址认证后即可访问。这段请求和权限之间的映射关系,会经过 DefaultFilterInvocationSecurityMetadataSource 的子类 ExpressionBasedFilterInvocationSecurityMetadataSource 进行处理,并最终将映射关系保存到 requestMap 变量中,以备后续使用。
在实际开发中,URL 地址以及访问它所需要的权限可能保存在数据库中,此时我们可以自定义类实现 FilterInvocationSecurityMetadataSource 接口,然后重写里边的 getAttributes 方法,在 getAttributes 方法中,根据当前请求的 URL 地址去数据库中查询其所需要的权限,然后将查询结果封装为相应的 ConfigAttribute 集合返回即可。
如果是基于方法的权限管理,那么对应的 MethodSecurityMetadataSource 实现类就比较多了:
-
PrePostAnnotationSecurityMetadataSource: @PreAuthorize、@PreFilter、@PostAuthorize、@PostFilter 四个注解所标记的权限规则,将由 PrePostAnnotationSecurityMetadataSource 负责提供。
-
SecuredAnnotationSecurityMetadataSource:@Secured 注解所标记的权限规则,将由 SecuredAnnotationSecurityMetadataSource 负责提供。
-
MapBasedMethodSecurityMetadataSource:基于 XML 文件配置的方法权限拦截规则(基于 sec:protect 节点),将由 MapBasedMethodSecurityMetadataSource 负责提供。
-
Jsr250MethodSecurityMetadataSource:JSR-250 相关的注解(如 @PermitAll、@DenyAll)所标记的权限规则,将由 Jsr25oMethodSecurityMetadataSource 负责提供。
这就是 SecurityMetadataSource 的作用。总之,不同的权限拦截方式都对应了一个 SecurityMetadataSource 实现类,请求的 URL 或者方法需要什么权限,调用 SecurityMetadataSource#getAttributes 方法就可以获取到。
权限表达式
Spring Security 3.0 引入了 SpEL 表达式进行权限配置,我们可以在请求的 URL 或者访问的方法上,通过 SpEL 来配置需要的权限。
内置的权限表达式如表13-1所示。


这是 Spring Security 内置的表达式,一般来说就足够使用了。如果这些内置的表达式无法满足项自需求,开发者可以自定义表送式,后面会介绍自定义表送式的方式。
Spring Security 中通过 SecurityExpressionOperations 接口定义了基本的权限表达式,代码如下:
public interface SecurityExpressionOperations {
Authentication getAuthentication();
boolean hasAuthority(String authority);
boolean hasAnyAuthority(String... authorities);
boolean hasRole(String role);
boolean hasAnyRole(String... roles);
boolean permitAll();
boolean denyAll();
boolean isAnonymous();
boolean isAuthenticated();
boolean isRememberMe();
boolean isFullyAuthenticated();
boolean hasPermission(Object target, Object permission);
boolean hasPermission(Object targetId, String targetType, Object permission);
}
返回值为 boolean 类型的就是权限表达式,如果返回 true,则表示权限校验通过,否则表示权限校验失败。
SecurityExpressionOperations 的实现类如图13-8所示。

SecurityExpressionRoot
SecurityExpressionRoot 对 SecurityExpressionOperations 接口做了基本的实现,并在此基础上增加了 principal。
接口的实现原理都很简单,这里就不展示源码了,我们说一下实现思路。
hasAuthority、hasAnyAuthority、hasRole 以及 hasAnyRole 四个方法主要是将传入的参数和 authentication 对象中保存的用户权限进行比对,如果用户具备相应的权限就返回 true,否则返回 false。permitAll 方法总是返回 true,而 denyAll 方法总是返回 false。isAnonymous、isAuthenticated、isRememberMe 以及 isFullyAuthenticated 四个方法则是根据对 authentication 对象的分析,然后返回 true 或者 false。最后的 hasPermission 则需要调用 PermissionEvaluator 中对应的方法进行计算,然后返回 true 或者 false。
SecurityExpressionRoot 中定义的表达式既可以在基于 URL 地址的权限管理中使用,也可以在基于方法的权限管理中使用。
WebSecurityExpressionRoot
WebSecurityExpressionRoot 继承自 SecurityExpressionRoot,并增加了 hasIpAddress 方法,用来判断请求的 IP 地址是否满足要求。
在 Spring Security 中,如果我们的权限管理是基于 URL 地址的,那么使用的就是 WebSecurityExpressionRoot,换句话说,这时可以使用 hasIpAddress 表达式。
MethodSecurityExpressionOperations
MethodSecurityExpressionOperations 定义了基于方法的权限管理时一些必须实现的接口,主要是参数对象的 get/set、返回对象的 get/set 以及返回受保护的对象。
MethodSecurityExpressionRoot
MethodSecurityExpressionRoot 实现了 MethodSecurityExpressionOperations 接口,并对其定义的方法进行了实现。MethodSecurityExpressionRoot 虽然也继承自 SecurityExpressionRoot,但是并未扩展新的表达式,换句话说,SecurityExpressionRoot 中定义的权限表达式在方法上也可以使用,但是 hasIpAddress 不可以在方法上使用。
通过上面的介绍,相信大家对 Spring Security 中权限相关概念有一个基本认知了,接下来我们就来看看基于 URL 地址的权限管理和基于方法的权限管理。