1 Star 0 Fork 0

方温南/学习文档

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
SpringSecurity 12.40 KB
一键复制 编辑 原始数据 按行查看 历史
fangwn 提交于 2021-12-29 15:53 +08:00 . aaa
一.核心组件
1.1.SecurityContextHolder
用于存储安全上下文信息.当前操作用户是谁,该用户是否已经认证,拥有的角色权限.
默认使用ThreadLocal策略来存储认证信息,这是一种与线程绑定的策略.
Spring Security在用户登录时自动绑定认证信息到当前线程,在用户退出时,自动清除当前线程的认证信息.但是这些都是在web环境下的.
在Swing界面中,Spring也提供了支持,SecurityContextHolder的策略则需要被替换.
获取当前用户信息
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
1.2.Authentication
a.Authentication在spring security中是最高级别的身份/认证的抽象
b.由这个顶级接口,我们可以得到用户拥有的权限信息列表,密码,用户细节信息,用户身份信息,认证信息
方法:
getAuthorities:权限信息列表,默认是GrantedAuthority接口的一些实现类,通常是代表权限信息的一系列字符串
getCredentials:密码信息,用户输入的密码字符串,在认证过后通常会被移除,用于保障安全
getDetails:细节信息,web应用中的实现接口通常为 WebAuthenticationDetails,它记录了访问者的ip地址和sessionId的值
getPrincipal:最重要的身份信息,大部分情况下返回的是UserDetails接口的实现类,也是框架中的常用接口之一
...
认证过程:
1.用户名和密码被过滤器获取到,封装成Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类
2.AuthenticationManager 身份管理器负责验证这个Authentication
3.认证成功后,AuthenticationManager身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会
被移除)Authentication实例
4.SecurityContextHolder安全上下文容器将第3步填充了信息的Authentication,通过SecurityContextHolder.getContext()
.setAuthentication(…)方法,设置到其中
1.3.AuthenticationManager
是认证相关的核心接口,也是发起认证的出发点
因为可能有多种认证方式,比如用户名密码,手机号和验证码,指纹登录,所以说AuthenticationManager一般不直接认证
AuthenticationManager接口的常用实现类ProviderManager内部会维护一个List<AuthenticationProvider>
列表,存放多种认证方式,实际上这是委托者模式的应用(Delegate).
也就说说核心的认证入口只有一个AuthenticationManager,不同的认证方式:用户名+密码(UsernamePasswordAuthenticationToken)
邮箱+密码,手机号码+密码登录则对应了三个AuthenticationProvider
ProviderManager中的list会依次认证,认证成功立即返回,失败返回null,下一个会继续认证.如果所有认证器都无法认证成功
则ProviderManager 会抛出一个ProviderNotFoundException异常
1.4.DaoAuthenticationProvider
获取用户提交的用户名和密码,比对其正确性,如果正确,返回一个数据库中的用户信息(假设用户信息被保存在数据库中)
1.5.UserDetails与UserDetailsService
UserDetails代表了最详细的用户信息,这个接口涵盖了一些必要的用户信息字段,具体的实现类对它进行了扩展。
和Authentication接口很类似,比如它们都拥有username,authorities,区分它们挺难的.
Authentication的getCredentials()与UserDetails中的getPassword()需要被区分对待,前者是用户提交的密码凭证,后者是用户正确的密码,
认证器其实就是对这两者的比对.Authentication中的getAuthorities()实际是由UserDetails的getAuthorities()传递而形成的
Authentication接口中UserDetails用户详细信息便是经过了AuthenticationProvider之后被填充的
UserDetailsService和DaoAuthenticationProvider两者的职责经常被搞混
UserDetailsService只负责从特定的地方(通常是数据库)加载用户信息,仅此而已,记住这一点,可以避免走很多弯路。UserDetailsService常见的实现类有JdbcDaoImpl,InMemoryUserDetailsManager,前者从数据库加载用户,后者从内存中加载用户,也可以自己实现UserDetailsService,通常这更加灵活
二.核心配置解读
1.@EnableWebSecurity
是个组合注解
@Import({ WebSecurityConfiguration.class, // <2>
SpringWebMvcImportSelector.class }) // <1>
@EnableGlobalAuthentication // <3>
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
1.SpringWebMvcImportSelector注解的作用是判断当前的环境是否springmvc
2.WebSecurityConfigura用来配置web安全的
3.EnableGlobalAuthentication
@EnableWebSecurity完成的工作便是加载了WebSecurityConfiguration,AuthenticationConfiguration这两个核心配置类,也就此将spring security的职责划分为了配置安全信息,配置认证信息两部分
AuthenticationConfiguration:
AuthenticationConfiguration的主要任务,便是负责生成全局的身份认证管理者AuthenticationManager
三.核心过滤器源码解读
3.1.spring security的过滤器日志有一个特点:log打印顺序与实际配置顺序符合,也就意味着SecurityContextPersistenceFilter是整个过滤器链的第一个过滤器,而FilterSecurityInterceptor则是末置的过滤器
Creating filter chain: o.s.s.web.util.matcher.AnyRequestMatcher@1,
[o.s.s.web.context.SecurityContextPersistenceFilter@8851ce1,
o.s.s.web.header.HeaderWriterFilter@6a472566,
o.s.s.web.csrf.CsrfFilter@61cd1c71,
o.s.s.web.authentication.logout.LogoutFilter@5e1d03d7,
o.s.s.web.authentication.UsernamePasswordAuthenticationFilter@122d6c22,
o.s.s.web.savedrequest.RequestCacheAwareFilter@5ef6fd7f,
o.s.s.web.servletapi.SecurityContextHolderAwareRequestFilter@4beaf6bd,
o.s.s.web.authentication.AnonymousAuthenticationFilter@6edcad64,
o.s.s.web.session.SessionManagementFilter@5e65afb6,
o.s.s.web.access.ExceptionTranslationFilter@5b9396d3,
o.s.s.web.access.intercept.FilterSecurityInterceptor@3c5dbdf8
]
3.2.过滤器作用:
SecurityContextPersistenceFilter:
a.请求来临时创建SecurityContext安全上下文信息
b.请求结束时清空SecurityContextHolder
HeaderWriterFilter:
用来给http响应添加一些Header,比如X-Frame-Options,X-XSS-Protection,X-Content-Type-Options
CsrfFilter:
用于防止csrf攻击
LogoutFilter:
处理注销的过滤器
UsernamePasswordAuthenticationFilter:
对提交的用户名和密码进行认证
RequestCacheAwareFilter:
内部维护了一个RequestCache,用于缓存request请求
SecurityContextHolderAwareRequestFilter:
对ServletRequest进行了一个包装,使得request具有更加丰富的API
AnonymousAuthenticationFilter:
匿名身份过滤器,spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份
SessionManagementFilter:
和session相关的过滤器,内部维护了一个SessionAuthenticationStrategy,两者组合使用,常用来防止session-fixation protection attack,以及限制同一用户开启多个会话的数量
ExceptionTranslationFilter:
异常翻译过滤器,本身不处理异常,而是交给内部维护的一些类去处理
FilterSecurityInterceptor:
决定了访问特定路径应该具备的权限,访问的用户的角色,权限是什么?访问的路径需要什么样的角色和权限?
3.3.SecurityContextPersistenceFilter:
用户在登录过一次之后,后续的访问便是通过sessionId来识别,从而认为用户已经被认证。具体在何处存放用户信息,便是第一篇文章中提到的SecurityContextHolder
认证相关的信息是如何被存放到其中的,便是通过SecurityContextPersistenceFilter
SecurityContextPersistenceFilter的两个主要作用便是请求来临时,创建SecurityContext安全上下文信息和请求结束时清空SecurityContextHolder
微服务的一个设计理念需要实现服务通信的无状态,而http协议中的无状态意味着不允许存在session,这可以通过setAllowSessionCreation(false)实
现,这并不意味着SecurityContextPersistenceFilter变得无用,因为它还需要负责清除用户信息
3.4.UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilter主要肩负起了调用身份认证器,校验身份的作用
内部调用了authenticationManager完成认证,根据认证结果执行successfulAuthentication或者unsuccessfulAuthentication,无论成功失败,一般的实现都是转发或者重定向等处理
3.5.AnonymousAuthenticationFilter
其实对比AnonymousAuthenticationFilter和UsernamePasswordAuthenticationFilter就可以发现一些门道了,UsernamePasswordAuthenticationToken对应AnonymousAuthenticationToken
他们都是Authentication的实现类,而Authentication则是被SecurityContextHolder(SecurityContext)持有的,一切都被串联在了一起
3.6.ExceptionTranslationFilter
ExceptionTranslationFilter异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常,将其转化,顾名思义,转化以意味本身并不处理
一般其只处理两大类异常:AccessDeniedException访问异常和AuthenticationException认证异常
这个过滤器非常重要,因为它将Java中的异常和HTTP的响应连接在了一起,这样在处理异常时,我们不用考虑密码错误该跳到什么页面,账号锁定该如何,只需要关注自己的业务逻辑,抛出相应的异常便可
如果该过滤器检测到AuthenticationException,则将会交给内部的AuthenticationEntryPoint去处理,如果检测到AccessDeniedException,需要先判断当前用户是不是匿名用户,如果是匿名访问
则和前面一样运行AuthenticationEntryPoint,否则会委托给AccessDeniedHandler去处理,而AccessDeniedHandler的默认实现,是AccessDeniedHandlerImpl。所以ExceptionTranslationFilter
内部的AuthenticationEntryPoint是至关重要的,顾名思义:认证的入口点
AuthenticationEntryPoint和AccessDeniedHandler
AccessDeniedHandlerImpl这个默认实现类会根据errorPage和状态码来判断,最终决定跳转的页面
3.7.FilterSecurityInterceptor
FilterSecurityInterceptor从SecurityContextHolder中获取Authentication对象,然后比对用户拥有的权限和资源所需的权限。前者可以通过Authentication对象直接获得
而后者则需要引入我们之前一直未提到过的两个类:SecurityMetadataSource,AccessDecisionManager
四.SpringSecurityFilterChain加载流程深度解析
4.1.SpringSecurityFilterChain是怎么注册的
a.java配置方式
1.作为独立的SpringSecurity依赖提供给朴素的java web项目使用,并且项目不使用Spring!没错,仅仅使用servlet,jsp的情况下也是可以集成SpringSecurity的
2.提供给包含SpringMVC项目使用
3.提供给具备Servlet3.0+的web项目使用
4.SpringBoot 内嵌容器环境下使用 SpringSecurity,并且包含了一定程度的自动配置
b.XML配置方式
1.使用XML中的命名空间配置SpringSecurity
SpringSecurityFilterChain 抽象概念里最重要的三个类:DelegatingFilterProxy,FilterChainProxy 和 SecurityFilterChain
不同环境下 DelegatingFilterProxy 的注册方式区别较大,但 FilterChainProxy 和 SecurityFilterChain 的差异不大
在 servlet3.0 环境下,web 容器启动时会自行去寻找类路径下所有实现了 WebApplicationInitializer 接口的 Initializer 实例
并调用他们的 onStartup 方法。所以,我们只需要继承 AbstractSecurityWebApplicationInitializer ,便可以自动触发 web 容器的加载
进而配置和 SpringSecurityFilterChain 第一个密切相关的类,第<2>步中的 DelegatingFilterProxy
DelegatingFilterProxyRegistrationBean其作用便是在 SpringBoot 环境下通过 TomcatStarter 等内嵌容器启动类来注册一个 DelegatingFilterProxy
4.2.SpringSecurityFilterChain三个核心类的源码分析
理解 SpringSecurityFilterChain 的工作流程必须搞懂三个类:
org.springframework.web.filter.DelegatingFilterProxy
org.springframework.security.web.FilterChainProxy
org.springframework.security.web.SecurityFilterChain
4.3.DelegatingFilterProxy
它本身是Spring Web包中的类,并不是SpringSecurity中的类
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Java
1
https://gitee.com/nanwenfang/learning_documents.git
git@gitee.com:nanwenfang/learning_documents.git
nanwenfang
learning_documents
学习文档
master

搜索帮助