spring security 鉴权
无论是否使用spring-security的认证, 都可以使用spring-security的鉴权功能。
鉴权架构
权限 Authorities
认证的时候AuthenticationManager会把授予主体的权限(GrantedAuthoritys)存进Authentication里。后面用户访问应用资源的时候, AccessDecisionManager 会根据GrantedAuthority做鉴权, 判断用户是否有权限访问此资源。
GrantedAuthority 是一个接口, 只有一个方法:
1 | String getAuthority(); |
AccessDecisionManager调用此方法等到一个String来表示权限。如果GrantedAuthority实现类不能转为String 应该返回null。
GrantedAuthority有一个具体实现SimpleGrantedAuthority,可以把指定字符串转为 GrantedAuthority。spring security 认证体系中的所有AuthenticationProvider 都是用此类来填充Authentication对象。
预调用处理
spring security 提供有拦截器来控制对安全对象(如:方法、web请求等)访问, 通过AccessDecisionManager 来决定是否可以访问。
The AccessDecisionManager
AccessDecisionManager 负责做访问控制的决策,由AbstractSecurityInterceptor调用。AccessDecisionManager接口包含以下三个方法:
1 | void decide(Authentication authentication, Object secureObject, |
decide 方法的参数中包含了和授权决定相关的所有信息。特别是secureObject包含了安全对象被调用的时候的所有参数(比如方法调用参数, web请求的参数), 如果拒绝访问的话会抛出AccessDeniedException异常。
supports(ConfigAttribute) 方法由 AbstractSecurityInterceptor 在鉴权开始时调用,以确定 AccessDecisionManager 是否可以处理传入的 ConfigAttribute。
supports(Class) 表示此AccessDecisionManager 是否能处理指定的secureObject 类。
基于投票的 AccessDecisionManager 实现
用户实现自己的AccessDecisionManager 来鉴权的各个方面。
Spring security 自身包含了几个基于投票的AccessDecisionManager实现。
投票鉴权相关的类:

这种方式下, 一次鉴权过程中, 一系列的AccessDecisionVoter(选票)会被轮询。AccessDecisionManager 然后根据投票评估决定是否抛出 AccessDeniedException异常。
AccessDecisionVoter有以下三个方法:
1 | int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attrs); |
vote返回一个int,可能的值定义在AccessDecisionVoter静态常量字段里:ACCESS_ABSTAIN,ACCESS_DENIED and,ACCESS_GRANTED,分别表示弃权、拒绝、同意。
AccessDecisionManager 有三种具体的投票决定实现:
ConsensusBased:共识方案, 少数服从多数。 有属性可以控制票数一样或者全部弃权的情况怎么处理AffirmativeBased: 可决票方案。 只要有一个赞成票就通过。有属性可以控制全部弃权的情况怎么处理UnanimousBased:一致性方案。 一票反对就拒绝(并非要求全票通过, 可以有弃权票)。有属性可以控制全部弃权的情况怎么处理
RoleVoter 角色投票人
RoleVoter 是spring security 提供的一种AccessDecisionVoter, 是最常用的一咱AccessDecisionVoter, 顾名思义就是以角色作为判断标准来投票。它把参数Collection<ConfigAttribute> attrs的每个ConfigAttribute都当作 角色名 来处理,如果用户有这个角色,就授权。
如果有任意ConfigAttribute是以ROLE_开头,RoleVoter就会投票。如果主体有GrantedAuthority 和一个或者多个ROLE_开头的ConfigAttribute相等,就投赞成票。 如果没有就投反对票。如果没有ROLE_开头的ConfigAttribute就弃权。
AuthenticatedVoter 认证投票人
根据身份认证的情况来投票,可以区分匿名(anonymous)、完全认证(fully-authenticated)、记住我(remember-me)方式认证。
使用IS_AUTHENTICATED_ANONYMOUSLY来授权访问时,AuthenticatedVoter 会参与投票。
Custom Voters自定义投票人
可实现自己的AccessDecisionVoter 来定制访问控制逻辑。示例blog。
After Invocation Handling(ACL) 调用后处理
AccessDecisionManager 在进行安全对象调用之前由 AbstractSecurityInterceptor 调用, 负责调用前处理。但是有些应用需要的安全对象调用后,修改其返回内容。虽然可以通过AOP方式实现, 但Spring Security 提供了一个方便的钩子,提供调用后处理的能力, 它有几个具体实现:

AfterInvocationManager 有一个简单的具体实现AfterInvocationProviderManager, 它通过轮询AfterInvocationProvider列表来实现具体功能。每个AfterInvocationProvider都可以修以返回内容, 或者抛出AccessDeniedException异常。可以多个AfterInvocationProvider都修改返回内容, 上一个修改的内容会 作为参数传递给下一个。
Hierarchical Roles 角色分层
支持角色的继承关系,RoleHierarchyVoter。
FilterSecurityInterceptor做HttpServletRequest鉴权
本节讨论鉴权的Servlet下如何实现的。
FilterSecurityInterceptor 为 HttpServletRequests 提供鉴权。它作为Security Filters之一插入到 FilterChainProxy 中。

首先,
FilterSecurityInterceptor从SecurityContextHolder获取一个Authentication表示当前用户(不一定登录了)然后,
FilterSecurityInterceptor根据传入的HttpServletRequest,HttpServletResponse, andFilterChain构建一个FilterInvocation。接着,把
FilterInvocation传给SecurityMetadataSource以获取ConfigAttribute列表。最后,把
1
Authentication
、
1
FilterInvocation
、
1
ConfigAttribute
传给
1
AccessDecisionManager.decide()
方法。
- 如果鉴权拒绝,抛出
AccessDeniedException异常,ExceptionTranslationFilter会处理这个异常。 - 如果有权限,
FilterSecurityInterceptor调用过滤链中的下一个过滤器。
- 如果鉴权拒绝,抛出
默认情况下, Spring Security要求所有的请求都通过身份认证,显示配置类似:
1 | protected void configure(HttpSecurity http) throws Exception { |
我们可以通过按优先顺序来添加更多的的规则:
1 | protected void configure(HttpSecurity http) throws Exception { |
- 这里配置了多个鉴权规则, 按定义的顺序处理, 这里配置的规则是就前面提到的
ConfigAttribute。 - 可匹配多个URL模式。
permitAll方法授予所有人权限。以”/resources” 开头的,以及等于”/signup”, “/about” 的URL所有人都可以访问。 - 使用
hasRole方法。“/admin” 开头的URL必须要有“ROLE_ADMIN” 角色才能访问。如果用hasRole方法,参数不需要ROLE_前缀 - 使用鉴权表达式。 鉴权表大式里
hasRole也不需要ROLE_前缀。 anyRequest表示全部请求。denyAll拒绝所有人访问。
Expression-Based Access Control 鉴权表达式
除了上面提到的配置方式外,Spring Security 3 开始还可以使用Spring EL表达式作为一种鉴权机制。 鉴权表达式也是构建这相同架构之上的,但是可以用一个表达式处理更复杂的逻辑判断。
概览
Spring Security 以Spring EL为基础,如果有兴趣更深入地理解该主题,应该看看它是如何工作的。 表达式使用一个 根对象 作为计算上下文。Spring Security 为WEB请求和方法,分别提供了特定的类作为根对象(如:当前主体),表达式从其中取值进行计算。
Common Built-In Expressions 常用的内置表达式
| 描述 | 表达式 |
|---|---|
hasRole(String role) |
有此角色返回true。默认情况下如果参数指定的角色名没有以ROLE_开头,表达式会加上这个前缀,比如:hasRole("ADMIN")实际会判断ROLE_ADMIN角色。可通过设置DefaultWebSecurityExpressionHandler.defaultRolePrefix修改默认前缀。 |
hasAnyRole(String… roles) |
是否包含任一指定角色。前缀规则同上。 |
hasAuthority(String authority) |
是否有指定权限 |
hasAnyAuthority(String… authorities) |
是否有任一指定的权限 |
principal |
表达式里可以直接访问当前主体对象。 |
authentication |
表达式可直接访问当前Authentication对象 |
permitAll |
直接返回true |
denyAll |
直接返回false |
isAnonymous() |
是否是匿名用户 |
isRememberMe() |
是否是记住我登录 |
isAuthenticated() |
是否通过身份认证(非匿名用户), |
isFullyAuthenticated() |
是否是完全认证(非匿名、非记住我) |
hasPermission(Object target, Object permission) |
领域对象安全验证, 是否有访问target对象的某种权限。比如hasPermission(domainObject, 'read') |
hasPermission(Object targetId, String targetType, Object permission) |
领域对象安全验证,同上,不过对象是通过id和类型来指定的。如:hasPermission(1, 'com.example.domain.Message', 'read') |
Web Security Expressions Web安全表达式
//todo
Method Security Expressions 方法安全表达式
方法安全性比简单的允许或拒绝规则要复杂一些。 Spring Security 3.0 引入了一些新的注解,以便全面支持表达式的使用。
@Pre 和 @Post 注解
有四个注解支持表达式属性,以允许调用前和调用后的授权检查,还支持过滤提交的集合参数或返回值。分别是: @PreAuthorize, @PreFilter, @PostAuthorize 和 @PostFilter。
总得来说就是在方法上使用注解,来做鉴权。细节以后再看。