1. 什么是授权
Spring Security 是为web提供解决方案的安全框架,每一个安全框架都主要做两件事,认证和授权,在之前的章节中,已经给大家介绍了认证,今天主要是介绍授权;其实授权就是在认证之后,检验用户是否具有访问某一资源的权限;简单的说:认证就是你能干些什么。
2.Spring Security授权原理
当用户访问我们的资源,最后会交给叫 FilterSecurityInterceptor 的过滤器判断是否有访问的权限;下面是该过滤器的核心代码
protected InterceptorStatusToken beforeInvocation(Object object) {
// 获取访问URL所需权限
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
Authentication authenticated = authenticateIfRequired();
// 通过accessDecisionManager鉴权
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
accessDeniedException));
throw accessDeniedException;
}
if (debug) {
logger.debug("Authorization successful");
}
if (publishAuthorizationSuccess) {
publishEvent(new AuthorizedEvent(object, attributes, authenticated));
}
// Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
attributes);
if (runAs == null) {
if (debug) {
logger.debug("RunAsManager did not change Authentication object");
}
// no further work post-invocation
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
attributes, object);
}
else {
if (debug) {
logger.debug("Switching to RunAs Authentication: " + runAs);
}
SecurityContext origCtx = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
SecurityContextHolder.getContext().setAuthentication(runAs);
// need to revert to token.Authenticated post-invocation
return new InterceptorStatusToken(origCtx, true, attributes, object);
}
}
最后通过AccessDecisionManager 接口的 decide()方法用于决定authentication是否符合受保护对象要求的configAttributes ;AccessDecisionManager接口有三个实现类,每个实现类都维护着一组 AccessDecisionVoter ,它们之间采用投票机制;下面分别讲三个实现类:
AffirmativeBased: 一组AccessDecisionVoter 只要有一个投通过票,那么就通过。
ConsensusBased:在 一组AccessDecisionVoter中会比较通过票,和未通过票数,如果通过票数大于未通过票数则通过。
UnanimousBased:一组AccessDecisionVoter 只要有一个投未通过票,那么就未通过。
3.自定义授权
为了创建自己的授权管理器,我们可以通过集成AbstractAccessDecisionManager
public class MyAccessDecisionManager extends AbstractAccessDecisionManager {
public void decide( Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException{
User user = (User)authentication.getPrincipal();
logger.info("访问资源的用户为"+user.getUsername());
//如果访问资源不需要任何权限则直接通过
if( configAttributes == null ) {
return ;
}
Iterator<ConfigAttribute> ite = configAttributes.iterator();
//遍历configAttributes看用户是否有访问资源的权限
while( ite.hasNext()){
ConfigAttribute ca = ite.next();
String needRole = ((SecurityConfig)ca).getAttribute();
//ga 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。
for( GrantedAuthority ga: authentication.getAuthorities()){
if(needRole.trim().equals(ga.getAuthority().trim())){
return;
}
}
}
throw new AccessDeniedException("");
}
由于AccessDecisionManager最终是被FilterSecurityInterceptor所调用,所以需要有我们自己的FilterSecurityInterceptor。