spring security源码追踪理解(二)

一、前言

近期看了spring security相关的介绍,所以想从源码的角度加深下对该安全模块的理解。为了防止迷失在源码的追踪中,我们首先列出自己感兴趣的点,然后带着问题去阅读源码:

1)spring security的入口是哪个类

2)spring security工作流程是什么样的

3)spring security用户名密码的认证逻辑是什么样的,密码是加密后比对吗

4)spring security针对每个请求都要去查一次用户信息吗

5)spring security匿名访问是如何跨过认证授权的

6)spring security权限认证逻辑是什么

前三个感兴趣的点在第一篇文章中已经展现,这里我们从第四个点开始进行追踪查看。

二、spring security登录后获取用户信息

若依框架在登录成功后,会生成一个token,并将toekn放入缓存,如下:

这里要留意的点有两个,一是用户信息会刷新到缓存。二是最终返回的token是经过二次处理的,不是一开始生成的uuid。二者大体情况如下:

后续所有的请求只要附带token,就可以根据token到缓存中获取用户信息,具体实现是在JwtAuthenticationTokenFilter过滤器中(该过滤器是框架带的,不是spring security中的)实现的,我们进入看一下该过滤器的处理逻辑::

这里我们看下获取loginUser的逻辑:

可以看到其首先是从请求中获取token,然后经过一系列转换获取key,最后从缓存中获取登录用户信息。再回到JwtAuthenticationTokenFilter的doFilterInternal方法,可以看到后续还会对token的有效期进行判断。

结合login和JwtAuthenticationTokenFilter来说,spring security针对每次请求是会查一次用户信息,只不过只有登录是从数据库中获取,其余请求都是从缓存中获取。

三、spring security匿名访问

这里主要讲匿名访问的使用(因为这是若依的东西,所以我简略的介绍)。具体的生效是在权限认证中生效的,权限认证的过程在第四部分讲。

首先我们进入代码,看下如何设置匿名访问(这块以若依框架为例讲解,如果你用的不是该框架,可能没这种用法):

这里可以看到匿名访问的使用很简单,添加一个Anonymous注解就行,那么该注解是如何跟spring security框架关联上的呢,点击该注解类名,查看使用该注解的类,可以看到有个PermitAllUrlProperties类,我们点击进入查看:

可以看到该对象在项目初始化创建时,会查找所有该Anonymous注释的方法url,并放入到list集合中。我们再点击PermitAllUrlProperties类,查看有哪些类使用该对象:

可以看到只有SecurityConfig中使用,我们进入查看:

可以看到,在SecurityConfig中会把所有注释Anonymous注解的url添加到spring security的”白名单”中。所有该名单中的请求都不需要进行校验。

至此Anonymous注解和spring security成功关联上。

四、spring security权限认证逻辑

spring security权限认证主要是在最后一个过滤器FilterSecurityInterceptor中实现,我们debug看一下其处理逻辑:

其处理主要分三块,这里我们先看下过滤器执行前的处理逻辑是什么:

这里主要关注两个点,一是如何获取需要的权限信息,二是如何进行权限验证。这里我们先进入代码看下如何获取需要的权限信息:

这块的逻辑整体比较清晰,就是比较系统中记录的权限信息和当前请求进行匹配,匹配上了就返回对应的权限集合。这里大家肯定有个疑问,那就是requestMap中的值哪来的呢,为了不影响当前代码逻辑,我放第五小节讲。这里我们接着向下看:

可以看到当前请求被匹配上了,因为没有针对该请求单独配置,所以是被AnyRequestMatcher匹配的,顾名思义可以知道它是一个任意匹配器,可以匹配所有未特殊配置的请求。而该匹配器返回当前请求需要的权限信息是authenticated,顾名思义就是要有登录权限。我们接着向下看:

获取了当前请求需要的权限信息后,接着查询当前请求的登录信息,我们进入内容看下细节:

可以看到其先从上下文中获取登录信息,如果存在就返回(不存在就调用authenticationManager进行登录处理),登录信息在前面JwtAuthenticationTokenFilter(处理登录过情况)和AnonymousAuthenticationFilter(处理未登录情况)过滤器中生成,因为我们没有登录,所以会以匿名的方式进行访问。这一点也可以从返回的对象名中看出来。

有了当前请求需要的权限信息和当前的用户登录状态信息后,接下来就是权限认证了:

可以看到关键点就是this.accessDecisionManager.decide,它们对判断当前请求权限是否足够,如果不够就会抛出访问拒绝的异常。我们接着看下权限判断的代码:

我们接着看下权限判断的逻辑:

这里的评估逻辑很复杂,我尝试看了下,涉及了知识盲区,即使单独写一篇文章估计也写不完,所以为了防止迷失在源码的"海洋"中,这里我先追踪到这。我们直接看比对结果:

因为我们没有登录,所以评估返回的结果是false,该方法返回ACCESS_DENTED状态,我们接着向下看:

可以看到,权限认证不通过,deny标识值大于0,最后抛出异常。

至此,权限认证过程结束。

五、requestMap值何来(比较绕,可以不看)

这里我是通过反推出来的,但是我展示还是按照正常的顺序给大家讲。

首先项目初始化时会创建springSecurityFilterChain过滤器链对象:

然后经过如下方法栈:

最后到ExpressionUrlAuthorizationConfigurer对象的createMetadataSource方法中,在该方法中会生成requestMap,我们看代码:

这里我们看下如何获取的url映射:

可以看到该属性也是系统启动时初始化的。这里我们在该集合新增的点打上断点,看下其方法栈:

可以看到在SecurityConfig初始化时会将所有配置的url及其权限信息注册到系统中。这就是requestMap值的来源。而我们所用的任意请求匹配对象,也是在SecurityConfig声明的,如下:

至此,spring security的requestMap生成过程基本结束,可以看到里面有很多框架自带的内容,如果用的不是若依框架,可能跟该源码追踪流程不一样。

六、总结

1、在spring security中,每次请求都会获取用户信息,但是一般只有登录时才从数据库中获取,其它类型的请求通常根据token从缓存中获取。

2、JwtAuthenticationTokenFilter是自定义的过滤器类,不是spring security自带的,大家浏览源码的时候要根据自己的情况进行理解

3、spring security匿名访问可以通过Anonymous注释接口实现,当然也可以主动在spring security初始化配置中添加。

4、因为没有每个过滤器都仔细看一遍,所以一开始是没找到认证过滤器的,直到在网上搜了下才知道最后一个过滤器是认证过滤器。所以大家在阅读源码时如果不知道从哪下手,也可以先上网搜一下,不必非得一点点看。直接看我们感兴趣的点可能效率会更高。

5、该源码追踪仅供参考,具体要以自身的项目代码为准,比如网上有的文章介绍了UsernamePasswordAuthenticationFilter用于用户名密码认证,但是该过滤器在我的项目里是没用到的,因为若依框架提供了login方法,其用于用户名密码的认证,随后又用JwtAuthenticationTokenFilter过滤器来根据token获取用户信息。所以在整个安全认证流程中没有用到UsernamePasswordAuthenticationFilter过滤器。

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值