Springboot security cas源码陶冶-FilterSecurityInterceptor
前言:用户登录信息校验成功后,都会获得当前用户所拥有的全部权限,所以对访问的路径当前用户有无权限则需要拦截验证一发
Spring security过滤器的执行顺序
首先我们需要验证为啥FilterSecurityInterceptor
会在UsernamePassowrdAuthenticationFilter/CasAuthenticationFilter
之后,这里则可以去看下spring security包下的FilterComparator
的构造函数便可以得知
FilterComparator() {
int order = 100;
****
****
order += STEP;
put(CorsFilter.class, order);
order += STEP;
put(CsrfFilter.class, order);
order += STEP;
put(LogoutFilter.class, order);
order += STEP;
put(X509AuthenticationFilter.class, order);
order += STEP;
put(AbstractPreAuthenticatedProcessingFilter.class, order);
order += STEP;
filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
order);
order += STEP;
put(UsernamePasswordAuthenticationFilter.class, order);
order += STEP;
put(ConcurrentSessionFilter.class, order);
order += STEP;
filterToOrder.put(
"org.springframework.security.openid.OpenIDAuthenticationFilter", order);
order += STEP;
****
****
order += STEP;
put(AnonymousAuthenticationFilter.class, order);
order += STEP;
put(SessionManagementFilter.class, order);
order += STEP;
put(ExceptionTranslationFilter.class, order);
order += STEP;
put(FilterSecurityInterceptor.class, order);
order += STEP;
put(SwitchUserFilter.class, order);
}
另外FilterComparator#compare()
方法表明是按照order的从小到大排序,所以Filter的执行顺序便一目了然,重要的Filter执行顺序如下
LogoutFilter-->CasAuthenticationFilter-->
UsernamePasswordAuthenticationFilter-->
AnonymousAuthenticationFilter-->ExceptionTranslationFilter-->
FilterSecurityInterceptor
FilterSecurityInterceptor#doFilter()-执行逻辑
代码如下
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
进而查看下FilterSecurityInterceptor#invoke()
方法
//主要展示了具体的逻辑,涉及到父类方法的调用
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//对同一个请求的多次访问则放行
if ((fi.getRequest() != null)
&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
&& observeOncePerRequest) {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
else {
//第一次访问则需要拦截验证
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 {
//是否需要重新设置spring的安全上下文SecurityContext
super.finallyInvocation(token);
}
//处理@PostAuthorize and @PostFilter注解
super.afterInvocation(token, null);
}
}
下面针对父类的方法进行分析
AbstractSecurityInterceptor#beforeInvocation-执行主要校验工作
由于代码偏长,截取重要代码片段分析
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
***
***
//一般通过SecurityMetadataSource对象获取当前用户访问路径对应的角色
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
//为空则抛异常或者返回null
if (attributes == null || attributes.isEmpty()) {
if (rejectPublicInvocations) {
throw new IllegalArgumentException(
"Secure object invocation "
+ object
+ " was denied as public invocations are not allowed via this interceptor. "
+ "This indicates a configuration error because the "
+ "rejectPublicInvocations property is set to 'true'");
}
if (debug) {
logger.debug("Public object - authentication not attempted");
}
publishEvent(new PublicInvocationEvent(object));
return null; // no further work post-invocation
}
if (debug) {
logger.debug("Secure object: " + object + "; Attributes: " + attributes);
}
//如果没有验证过则抛出AuthenticationException异常
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage(
"AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"),
object, attributes);
}
//对于非token和非login请求
//一般都会有默认的AnonymousAuthenticationFilter使其不再校验
//所以此处一般不需要再次校验
Authentication authenticated = authenticateIfRequired();
// Attempt authorization 尝试授权
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
accessDeniedException));
//对于授权失败则会抛出异常,这个异常会由ExceptionTranslationFilter获取
throw accessDeniedException;
}
****
****
// 默认不处理,runAs返回null
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
attributes);
if (runAs == null) {
//直接返回
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);
}
}
小结
FilterSecurityInterceptor实现的作用有
SecurityMetadataSource对象来获取当前访问路径对应的角色集合
Collection<ConfigAttribute> attributes
AccessDecisionManager对象来对获取到的角色集合进行校验,与
Authentication.getAuthorities()
集合进行对照验证与授权过程中产生的异常
AuthenticationException
和AccessDeniedException
会被ExceptionTranslationFilter
拦截处理,从而请求casServer登录或者直接返回错误
作者:南柯问天
出处:http://www.cnblogs.com/question-sky/
本文版权归本人和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。