前言:用户登录信息校验成功后,都会获得当前用户所拥有的全部权限,所以对访问的路径当前用户有无权限则需要拦截验证一发

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实现的作用有

  1. SecurityMetadataSource对象来获取当前访问路径对应的角色集合Collection<ConfigAttribute> attributes

  2. AccessDecisionManager对象来对获取到的角色集合进行校验,与Authentication.getAuthorities()集合进行对照

  3. 验证与授权过程中产生的异常AuthenticationExceptionAccessDeniedException会被ExceptionTranslationFilter拦截处理,从而请求casServer登录或者直接返回错误

Springboot security cas源码陶冶-FilterSecurityInterceptor的更多相关文章

  1. Springboot security cas源码陶冶-ExceptionTranslationFilter

    拦截关键的两个异常,对异常进行处理.主要应用异常则跳转至cas服务端登录页面 ExceptionTranslationFilter#doFilter-逻辑入口 具体操作逻辑如下 public void ...

  2. Springboot security cas源码陶冶-CasAuthenticationFilter

    Springboot security cas整合方案中不可或缺的校验Filter类或者称为认证Filter类,其内部包含校验器.权限获取等,特开辟新地啃啃 继承结构 - AbstractAuthen ...

  3. Springboot security cas整合方案-原理篇

    前言:网络中关于Spring security整合cas的方案有很多例,对于Springboot security整合cas方案则比较少,且有些仿制下来运行也有些错误,所以博主在此篇详细的分析cas原 ...

  4. Springboot security cas整合方案-实践篇

    承接前文Springboot security cas整合方案-原理篇,请在理解原理的情况下再查看实践篇 maven环境 <dependency> <groupId>org.s ...

  5. Spring-shiro源码陶冶-DelegatingFilterProxy和ShiroFilterFactoryBean

    阅读源码有助于陶冶情操,本文旨在简单的分析shiro在Spring中的使用 简单介绍 Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能 web.xml配置Shiro环 ...

  6. 修改CAS源码是的基于DB的认证方式配置更灵活

    最近在做CAS配置的时候,遇到了数据源不提供密码等数据的情况下,怎样实现密码输入认证呢? 第一步:新建Java项目,根据假面算法生成CAS加密工具 出于保密需要不提供自定义的加密工具,在您的实际项目中 ...

  7. Spring-shiro源码陶冶-DefaultFilter

    阅读源码有助于陶冶情操,本文旨在简单的分析shiro在Spring中的使用 简单介绍 Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能 Apache Shiro自带的 ...

  8. SpringBoot自动配置源码调试

    之前对SpringBoot的自动配置原理进行了较为详细的介绍(https://www.cnblogs.com/stm32stm32/p/10560933.html),接下来就对自动配置进行源码调试,探 ...

  9. 调试CAS源码步骤

    1.先安装gradle2.eclipse安装gradle(sts)插件3.克隆cas源码 这一块需要很长时间4.gradle build 会遇到安装node.js 的模块 不存在的问题. 按提示解决就 ...

随机推荐

  1. [bzoj1999]树网的核

    从下午坑到网上..noip的数据太弱,若干的地方写挂结果还随便过= = 最坑的就是网上有些题解没考虑周全... 第一步是找直径,用两次bfs(或者dfs,Linux下系统栈挺大的..)解决.找出其中一 ...

  2. [bzoj1910] [Ctsc2002] Award 颁奖典礼

    应该是第一次写这种图形类的DP.. 一个“I”可以分成三个矩形..令f[1..3][i][j][k]表示第几个矩形,下边界为第i行的j~k列,的最大面积. 然后就是各种优化啊什么的...时间复杂度O( ...

  3. HDU-5421Victor and String

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5421 因为要在前面插字符,所以维护一个前缀链和后缀链,在同一棵回文树上搞,如果有某个最长回文后缀(或前缀) ...

  4. Linux /bin, /sbin, /usr/bin, /usr/sbin 区别

    在linux下我们经常用到的四个应用程序的目录是:/bin./sbin./usr/bin./usr/sbin    bin:  bin为binary的简写主要放置一些系统的必备执行档例如:cat.cp ...

  5. 【Git】Git基础操作

    repository:版本库又名仓库,可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改.删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以&q ...

  6. How to find missing USB Records?

    In my previously article "EnCase missed some USB activities in the evidence files", I ment ...

  7. element-ui中upload组件如何传递文件及其他参数

    最近项目用到了vuethink,里面集成了element-ui,之前一直用的是bootstrap框架,对js也是一知半解,然后也用过vue.js,但也是学的不通透的,然后就各种入坑. 下面就分析一下我 ...

  8. ObjectiveC 深浅拷贝学习

    在ObjC中,什么是深浅拷贝? 深浅拷贝分别指深拷贝和浅拷贝,即mutableCopy和copy方法. copy复制一个不可变对象,而mutableCopy复制一个mutable可变对象. 什么时候用 ...

  9. @RequestMapping 相关 spring

    *      * @param request HttpServletRequest     * @param delList 削除Idエスト     * @return 削除結果     * @th ...

  10. 写一个简单的配置文件和日志管理(shell)

    最近在做一个Linux系统方案的设计,写了一个之前升级服务程序的配置和日志管理. 共4个文件,服务端一个UpdateServer.conf配置文件和一个UpdateServer脚本,客户端一个Upda ...