上一篇《结合实际场景分析Spring Boot Security(2.x.x)认证过程》我们梳理了整个认证的过程,这一篇我们就来讲一讲鉴权的大致流程。
由于本人并没有自定义鉴权逻辑的需求,所以本文不涉及如何定制化鉴权过程
一、鉴权入口——FilterSecurityInterceptor
《结合实际场景分析Spring Boot Security(2.x.x)认证过程》提到的12个filter中,最后一个是FilterSecurityInterceptor,它就承担了鉴权的重任。

- 进入FilterSecurityInterceptor.class
public void invoke(FilterInvocation fi) throws IOException, ServletException {
if ((fi.getRequest() != null)
&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
&& observeOncePerRequest) {
// filter already applied to this request and user wants us to observe
// once-per-request handling, so don't re-do security checking
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
else {
// first time this request being called, so perform security checking
if (fi.getRequest() != null && observeOncePerRequest) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
}
// 1. 在真正调用/xxx之前的一些处理,其中包含调用 AccessDecisionManager 来验证当前已认证成功的用户是否有权限访问该资源
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
// 2. 调用/xxx
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, null);
}
}
- 进入InterceptorStatusToken token = super.beforeInvocation(fi);
...
// Attempt authorization
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}
...
三个参数分别代表:authenticated——认证的对象,object——url信息,attributes——是否需要认证

-
accessDecisionManager提供三种方案,默认选用一票通过制,具体规则可以看代码里的注释
image.png
/**
* This concrete implementation simply polls all configured
* {@link AccessDecisionVoter}s and grants access if any
* <code>AccessDecisionVoter</code> voted affirmatively. Denies access only if there
* was a deny vote AND no affirmative votes.
* <p>
* If every <code>AccessDecisionVoter</code> abstained from voting, the decision will
* be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to
* false).
* </p>
*
* @param authentication the caller invoking the method
* @param object the secured object
* @param configAttributes the configuration attributes associated with the method
* being invoked
*
* @throws AccessDeniedException if access is denied
*/
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
//默认只有一个voter:WebExpressionVoter.class
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
- 默认的唯一一个voter:WebExpressionVoter.class投票逻辑:
/**
* Voter which handles web authorisation decisions.
* @author Luke Taylor
* @since 3.0
*/
public class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation> {
private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();
public int vote(Authentication authentication, FilterInvocation fi,
Collection<ConfigAttribute> attributes) {
assert authentication != null;
assert fi != null;
assert attributes != null;
WebExpressionConfigAttribute weca = findConfigAttribute(attributes);
if (weca == null) {
return ACCESS_ABSTAIN;
}
EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication,
fi);
ctx = weca.postProcess(ctx, fi);
return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED
: ACCESS_DENIED;
}
有兴趣的朋友可以研究一下这个voter的角色判断细节。
以上就是鉴权的过程。
二、过程梳理
FilterSecurityInterceptor.class
-> super.beforeInvocation(fi)
-> accessDecisionManager.decide(authenticated, object, attributes)
-> (默认的accessDecisionManager是AffirmativeBased.class 一票通过制)
-> voter.vote(authentication, object, configAttributes)
结语
由于没有定制化开发鉴权的经验,所以本文只是梳理了spring boot security的鉴权过程,让大家对此有个大致的了解,如果需要添加自定义的鉴权过程,还需大家更深入的调研。
感谢这篇《Spring Security源码分析二:Spring Security授权过程》
网友评论