FilterSecurityInterceptor
这个过滤器决定了访问特定路径应该具备的权限,访问的用户的角色,权限是什么?访问的路径需要什么样的角色和权限?这些判断和处理都是由该类进行的
FilterSecurityInterceptor
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
//默认实现类:ExpressionBasedFilterInvocationSecurityMetadataSource
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//判断该请求是否已经被 FilterSecurityInterceptor 处理过了
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) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
}
//交由 AbstractSecurityInterceptor 校验
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, null);
}
}
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
}
AbstractSecurityInterceptor
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{
private boolean alwaysReauthenticate = false;
//默认实现类是 AffirmativeBased
private AccessDecisionManager accessDecisionManager;
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
final boolean debug = logger.isDebugEnabled();
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException(
"Security invocation attempted for object "
+ object.getClass().getName()
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
+ getSecureObjectClass());
}
//obtainSecurityMetadataSource()方法由 FilterSecurityInterceptor 实现,返回一个 ExpressionBasedFilterInvocationSecurityMetadataSource 对象。
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
//...
//其实就是调用SecurityContextHolder.getContext().getAuthentication()然后返回该对象。如果 Authentication 还没有认证过,会调用 AuthenticationManager 进行认证。除此之外该类的 alwaysReauthenticate 属性值如果为 true 都会导致重新调用 AuthenticationManager 进行认证
Authentication authenticated = authenticateIfRequired();
// Attempt authorization
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");
}
//是否发布认证成功的Spring容器事件
if (publishAuthorizationSuccess) {
publishEvent(new AuthorizedEvent(object, attributes, authenticated));
}
// Attempt to run as a different user
//尝试以不同的用户允许
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
attributes);
//一般情况下都runAs都会为null
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 {
//...
}
}
}
ExpressionBasedFilterInvocationSecurityMetadataSource
public class DefaultFilterInvocationSecurityMetadataSource implements
FilterInvocationSecurityMetadataSource {
//里面保存着用户配置的各种请求路径规则和认证规则
//requestMap的最后有一个匹配任何请求路径的成员,该成员对象的认证规则为'authenticated'即需要经过认证才可以访问。
private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
//ConfigAttribute指定该路径是否需要验证,一般需要认证是其内部属性authorizeExpression 为 'authenticated',如果是任何人可以访问,内部属性authorizeExpression 为 'permitAll'
//获取能匹配到对应接口路径的认证规则
public Collection<ConfigAttribute> getAttributes(Object object) {
final HttpServletRequest request = ((FilterInvocation) object).getRequest();
for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
.entrySet()) {
if (entry.getKey().matches(request)) {
return entry.getValue();
}
}
return null;
}
}
AffirmativeBased
public class AffirmativeBased extends AbstractAccessDecisionManager {
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
//getDecisionVoters():默认情况得到是存放着 一个 WebExpressionVoter 对象的列表
for (AccessDecisionVoter voter : getDecisionVoters()) {
//调用voter进行分析并返回投票结果
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;
}
}
//只要有一个 AccessDecisionVoter 对象偷拍了拒绝访问,就任务
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
// To get this far, every AccessDecisionVoter abstained
//到目前为止,每个AccessDecisionVoter都投了弃权票
//如果该类的不允许所有投票器弃权 (AbstractAccessDecisionManager#allowIfAllAbstainDecisions;默认为false,就不允许),就会抛出 AccessDeniedException 异常,表示拒绝访问
checkAllowIfAllAbstainDecisions();
}
}
WebExpressionVoter
public class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation>{
public int vote(Authentication authentication, FilterInvocation fi,
Collection<ConfigAttribute> attributes) {
assert authentication != null;
assert fi != null;
assert attributes != null;
//这里是从attributes中找到属于WebExpressionConfigAttribute类型ConfigAttribute对象,并将其转换成WebExpressionConfigAttribute。
//每个请求路径都可能会有多个ConfigAttribute对象,但是WebExpressionVoter所用到的只是 attributes 中的第一个属于WebExpressionConfigAttribute类型ConfigAttribute对象
WebExpressionConfigAttribute weca = findConfigAttribute(attributes);
//没有找到weca会导致WebExpressionVoter放弃投票,让其他投票器投票。
if (weca == null) {
return ACCESS_ABSTAIN;
}
//这里构建spring el表达式的上下文。该上下文的根对象的 SecurityExpressionRoot 对象
EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication,
fi);
ctx = weca.postProcess(ctx, fi);
//根据表达式调用 SecurityExpressionRoot 对象里的方法。如表达式为 'authenticated' 调用的方法就是 SecurityExpressionRoot # isAuthenticated() 方法;表达式为'permitAll',调用的方法式 SecurityExpressionRoot # permitAll()
//ACCESS_GRANTED 表示 允许访问
//ACCESS_DENIED 表示 拒绝访问
return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED
: ACCESS_DENIED;
}
}
关于spring-securiy注解的校验过程
注解 | 说明 | EL表达式 |
---|---|---|
@Secured | 用于判断是否具有角色的。能写在方法或类上 | 否 |
@PreAuthorize | 访问方法或类在执行之前先判断权限 | 是 |
@PostAuthorize | 方法或类执行结束后判断权限 | 是 |
这个注解并不是交给 FiterSecurityInterceptor
处理,而是交由MethodSecurityInterceptor
处理,MethodSecurityInterceptor
其实是 Spring AOP 提供的方法拦截器。
FiterSecurityInterceptor
和MethodSecurityInterceptor
都是AbstractSecurityInterceptor
的子类,并且校验逻辑都是交给AbstractSecurityInterceptor
实现。FiterSecurityInterceptor
和MethodSecurityInterceptor
都是只是控制校验的流程。
FiterSecurityInterceptor
和MethodSecurityInterceptor
的区别只是 前者是java web 过滤器,后者是 AOP的方法拦截器。
MethodSecurityInterceptor
public class MethodSecurityInterceptor extends AbstractSecurityInterceptor implements
MethodInterceptor {
//由AbstractSecurityInterceptor实现认证处理,这里只是控制层
public Object invoke(MethodInvocation mi) throws Throwable {
InterceptorStatusToken token = super.beforeInvocation(mi);
Object result;
try {
result = mi.proceed();
}
finally {
super.finallyInvocation(token);
}
return super.afterInvocation(token, result);
}
}