前言

在之前文章中有单独写过SpringSecurity表单登录流程源码分析SpringSocial使用QQ授权登录流程详细分析两篇关于登录流程的,这些其实只是SpringSecurity整个流程的一部分罢了,也就是验证一下用户身份,但是真正的授权还是要这篇文章来讲解的!废话不多说,进入正题吧!

SpringSecurity授权流程源码分析
这张图是贯穿整个SpringSecurity的核心流程的,但是观看图可能理解是有点抽象,这篇文章就来详细解释这张流程图!

理论铺垫

SpringSecurity真正判断请求能否通过是在FilterSecurityInterceptor过滤器中处理的,如果请求不能通过的话就会根据不同的原因抛出异常,抛出异常以后就会由ExceptionTranslationFilter这个过滤器接收处理,这两个就是整个SpringSecurity授权相关的核心类,这里还有一个比较特殊的过滤器,就是最后一个绿色的过滤器AnonymousAuthenticationFilter,顾名思义这个过滤器是匿名的过滤器,这个过滤器先记着因为比较特殊,在下文中会详细解释这是做什么用的

特殊的AnonymousAuthenticationFilter过滤器
进入源代码看看
SpringSecurity授权流程源码分析

整个逻辑代码很简单,就是判断当前的SecurityContextHolder中是否有authentication,是否等于null,这里判断是否有authentication实际上就是在判断前面绿色的过滤器是否完成身份的认证,或者是在Session中拿到身份认证信息,如果前面的过滤器一个都没有认证的话那么就会创建一个authentication然后set到SecurityContextHolder中,这里实际上放到SecurityContextHolder中的Authentication实际上并不是之前过滤器认证成功后的用户信息,而是一个字符串
SpringSecurity授权流程源码分析
也就是这个字符串
SpringSecurity授权流程源码分析
在上面的SpringSecurity整个流程图中,AnonymousAuthentication过滤器是在整个绿色过滤器的最后一个,也就是不管你前面有没有经过身份认证成功,SecurityContextHolder中一定是会放一个Authentication的,如果前面的过滤器有一个认证成功了,那么Authentication就是用户身份信息,如果前面过滤器一个都没有认证成功,那么Authentication就是一个字符串!那么当请求到达AnonymousAuthenticationFilter这个匿名过滤器之后我们的SecurityContextHolder中就一定会有一个Authentication,那么这个Authentication最后就会传给FilterSecurityInterceptor这个过滤器,也就是SpringSecurity整个过滤器连上的最后一个过滤器,由FilterSecurityInterceptor过滤器来决定当前的Authentication对象所包含的权限是否可以访问当前请求的URL

最后的护城门FilterSecurityInterceptot过滤器
在分析FilterSecurityInterceptot的源码是先看一张流程图
SpringSecurity授权流程源码分析
这张图看起来是挺复杂的,但实际上是很简单,最核心的也就是FilterSecurityInterceptor、AccessDecisionManager、AccessDecisionVoter。

FilterSecurityInterceptor:它是这个授权的主入口,也就是护城门虽然这里它是Interceptor结尾,但是他实际上是个过滤器,也就是SpringSecurity过滤器中的最后一个,AccessDecisionManager:访问决定的管理者,这个是一个接口,它有一个抽象的实现(AbstractAccessDecisionManager)和三个具体的实现(AffirmativeBased、ConsensusBased、UnanimousBased),那么这个AccessDecisionManager实际上是管理着一组AccessDecisionVoter,从AccessDecisionVoter名字就能看出它是做投票的意思,在SpringSecurity3.X之前是有一组AccessDecisionVoter的实现(在SpringSecurity3之后框架升级了这么一组AccessDecisionVoter的实现就变成了一个WebExpressionVoter包办了一组AccessDecisionVoter的所有工作),不同的实现用来处理不同的判断逻辑,当AccessDecisionManager收到一个请求时这一组的AccessDecisionVoter会根据自己的逻辑判断这个请求是过还是不过,那么在AccessDecisionManager里就会中和这一组AccessDecisionVoter的投票给出一个最终的结果,那么判断最终过还是不过有三套逻辑,也就是(AffirmativeBased、ConsensusBased、UnanimousBased)的实现里面,(AffirmativeBased的判断逻辑:不管有多少个AccessDecisionVoter投不过只要有一个AccessDecisionVoter投过那么整个请求就投过可以访问)、(UnanimousBased的判断逻辑:不管有多少个AccessDecisionVoter投通过,只要有一个AccessDecisionVoter投不过那么请求就不可以访问),(ConsensusBased的判断逻辑:就是判断通过的AccessDecisionVoter多还是不通过的AccessDecisionVoter多,那个多就是那个),在SpringSecurity的默认实现逻辑中使用的是第一种,也就是(AffirmativeBased的判断逻辑:不管有多少个AccessDecisionVoter投不过只要有一个AccessDecisionVoter投过那么整个请求就投过可以访问)

在介绍完这三个核心后还要介绍一下ConfigAttribute,Authentication,在判断一个请求能不能过需要两块数据,第一块就是请求的权限信息ConfigAttribute(a请求需要什么样的权限,b请求需要什么样的权限),那么这些配置信息就是权限信息
SpringSecurity授权流程源码分析
请求到达FilterSecurityInterceptor过滤器时就会把这些请求路径所需的权限读出来,封装成一组ConfigAttribute对象,这一组对象实际上就是每一个请求路径都会对应一个权限,那么ConfigAttribute就是整个系统的路径权限配置信息,

那么判断请求能不能通过的另一块信息就是Authentication,Authentication实际上就是当前请求的用户的权限信息,也就是当前用户有哪些权限,这个信息就是封装在我们的Authentication里面的

那么一块系统请求URL权限信息,一块用户权限信息,还有请求进来的请求信息如当前请求的URL是什么,这样三方数据传给我们的AccessDecisionManager然后AccessDecisionManager在把这些信息给到投票者来投票当前请求过还是不过,然后根据最后的决定器判断当前请求过还是不过这就是整个授权控制的主流程

断点走起

FilterSecurityInterceptor过滤器
SpringSecurity授权流程源码分析
invoke()方法
SpringSecurity授权流程源码分析
这个方法invoke()方法的第一块逻辑就是判断当前请求有没有经过认证过滤器,如果经过安全过滤器判断了就不在重复判断了,直接往下走了这里的判断依据就是fi.getRequest().getAttribute(FILTER_APPLIED)是否有值,当前我访问的是未登录的状态,所以这里第一块逻辑无法进入,那么我们进入第二块逻辑,进来后就把FILTER_APPLIED属性设上值。

SpringSecurity授权流程源码分析
这里圈起来的就是最关键的依据代码了,这里所谓的Invocation实际上就是调我们controller的接口了,那么在整个beforeInvocation方法中就实现了整个授权判断的逻辑,那么在这个beforeInvocation里面出了问题就会抛异常,如果不出问题那么就会往下调用我们的controller接口了。

beforeInvocation()授权判断
SpringSecurity授权流程源码分析
SpringSecurity授权流程源码分析
decide()方法
SpringSecurity授权流程源码分析
SpringSecurity默认实现是AffirmativeBased(只要有一个投票器投过那么请求就能通过)
SpringSecurity授权流程源码分析
这里异常会抛到ExceptionTranslationFilter过滤器上

ExceptionTranslationFilter过滤器
SpringSecurity授权流程源码分析
然后这里的response就会原路返回,浏览器收到响应后就会跳转到登录地址!那么整个授权的流程就分析完了,那么登录成功后的请求也就是投票器哪里会头过,其他流程也都是一样的!

相关文章: