SpringSecutiry-自定义决策
当用户身份认证通过后,会调用决策管理器判断是否可以继续访问,图中的AccessDecisionManager就是SpringSecurity的角色管理器,AbstractAccessDecisionManager,而我们要自定义角色管理器的话,一般是继承抽象类而不是去实现接口。
public boolean supports(ConfigAttribute attribute) {
for (AccessDecisionVoter voter : this.decisionVoters) {
if (voter.supports(attribute)) {
return true;
}
}
return false;
}
supports()方法是AbstractAccessDecisionManager的核心方法。方法中有一个decisionVoters,它的类型是一个AccessDecisionVoter。
public interface AccessDecisionVoter<S> {
// ~ Static fields/initializers
// =====================================================================================
int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = -1;
// ~ Methods
// =====================================================================================
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
int vote(Authentication authentication, S object,
Collection<ConfigAttribute> attributes);
}
AccessDecisionVoter是SpringSecurity引入的投票器的概念,权限访问的最终决定权,就是由投票器来决定的。Security里由许多投票器,最常见的投票器为RoleVoter。
public class RoleVoter implements AccessDecisionVoter<Object> {
// ~ Instance fields
// ========================================================================================
private String rolePrefix = "ROLE_";
// ~ Methods
// ===================================================================================
public String getRolePrefix() {
return rolePrefix;
}
/**
* Allows the default role prefix of <code>ROLE_</code> to be overridden. May be set
* to an empty value, although this is usually not desirable.
*
* @param rolePrefix the new prefix
*/
public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null)
&& attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
}
else {
return false;
}
}
/**
* This implementation supports any type of class, because it does not query the
* presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class<?> clazz) {
return true;
}
public int vote(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) {
if (authentication == null) {
return ACCESS_DENIED;
}
int result = ACCESS_ABSTAIN;
Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
for (ConfigAttribute attribute : attributes) {
if (this.supports(attribute)) {
result = ACCESS_DENIED;
// Attempt to find a matching granted authority
for (GrantedAuthority authority : authorities) {
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
Collection<? extends GrantedAuthority> extractAuthorities(
Authentication authentication) {
return authentication.getAuthorities();
}
}
在RoleVoter中,定义了权限的前缀ROLE_,投票器的核心方法是vote()。vote方法中,authentication是用户集权限信息,attributes是访问资源需要的权限,代码里循环判断用户是否有访问资源需要的权限,有则返回ACCESS_GRANTED。SpringSecurity目前提供了三个决策投票器,第一个是AffirmativeBased,它的策略是一票通过,只要有一个投票器通过就允许访问;第二个是ConConsensusBased,它的策略是有一半以上投票通过,才允许访问资源;第三个UnanimousBased,它的策略是所有投票器都通过,才允许访问资源。当我们自定义角色管理器时,我们继承AbstractAccessDecisionManager这个类即可,并不是一定要使用投票器,也可以根据需要使用自己的投票器。如果我们访问某个资源,需要同时拥有两个或两个以上权限的情况时,我们就需要自定义AccessDecisionVoter来实现。