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
。
总得来说就是在方法上使用注解,来做鉴权。细节以后再看。