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过滤器。
原文地址:https://blog.csdn.net/Interest1_wyt/article/details/140519334
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!