前面截图里的filter chain,最前面是我们的自定义filter来认证的,最后面的FilterSecurityInterceptor则是权限校验。
//spring-security-core-5.1.4.RELEASE-sources.jar!/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java:229 Authentication authenticated = authenticateIfRequired(); // Attempt authorization try { this.accessDecisionManager.decide(authenticated, object, attributes); } catch (AccessDeniedException accessDeniedException) { publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException)); throw accessDeniedException; }可以看到,调用accessDecisionManager来判断是否继续,权限不足则抛出AccessDeniedException,对应处理就是403了。
AccessDecisionManager目前看到有两种,一个是全局配置,在我们配置Security Config里指定哪些url需要哪些权限。一个是method级别的配置,通过前者校验后判断method是否有权限。
AbstractAccessDecisionManager提供了3种方式。
AffirmativeBased 任意一种权限校验voter方式通过即通过
UnanimousBased 必须所有voter通过才可以通过,即任意失败则不通过
ConsensusBased 通过的voter大于拒绝的voter则通过
其他,可以自己实现AbstractAccessDecisionManager
Voter是什么呢?AccessDecisionVoter是真正判断权限的地方。通过对比当前登录用户的authority权限和要访问的资源的权限比较,返回如下code。
int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = -1;
权限移除前缀ROLE_Spring Security默认使用ROLE_作为authority的前缀,然后表达式里的hasRole, hasAuthority几乎等价,这让我一直很困惑。尤其是当我使用user-role-permission模型的时候,差点以为hasRole是角色判断。所以,为了避免混淆,决定把ROLE_的前缀去掉。
方法就是声明一个类, 具体理由可以追寻源码hasRole来确定。
@Bean GrantedAuthorityDefaults grantedAuthorityDefaults() { return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix } 统一超级权限admin到现在差不多已经可以实现用户权限校验了。我们给需要权限敏感的api添加注解,比如@PreAuthorize("hasRole('can_list_user')"), 然后permission表里添加can_list_user, 然后角色表role绑定permission,最后把role指派给user。
然而,当系统需要权限的地方特别多的时候,绑定role的代价也很高。比如,我们需要一个超级管理员admin角色,那么这个admin就必须把所有的permission绑定一遍。想想就恐怖。
既然理解了Spring Security的权限校验方式,那么就可以自定义了。我们指定带有admin的authority直接通过,无需校验其他权限。
/** * 允许设计admin权限的用户直接通过所有认证 * @author Ryan Miao * @date 2019/6/12 20:49 */ @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration { @Override protected AccessDecisionManager accessDecisionManager() { List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends Object>>(); ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice(); expressionAdvice.setExpressionHandler(getExpressionHandler()); decisionVoters .add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice)); decisionVoters.add(new RoleVoter()); decisionVoters.add(new AuthenticatedVoter()); decisionVoters.add(new AdminVoter()); return new AffirmativeBased(decisionVoters); } } /** * 拥有admin权限的角色,直接包含所有权限 * * @author Ryan Miao * @date 2019/6/12 20:00 */ public class AdminVoter implements AccessDecisionVoter<Object> { private static final String ADMIN = "admin"; @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override 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 (ADMIN.equals(authority.getAuthority())) { return ACCESS_GRANTED; } } } } return result; } Collection<? extends GrantedAuthority> extractAuthorities( Authentication authentication) { return authentication.getAuthorities(); } @Override public boolean supports(Class clazz) { return true; } } 总结