目录
Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是Spring Security 重要核心功能。
(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问 该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认 证过程。通俗点说就是系统认为用户是否能登录
(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户 所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以 进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的 权限。通俗点讲就是系统判断用户是否有权限去做某些事情。
helloworld:
使用springInitizer创建项目,勾选上web、springsecurity。
1.创建配置类,继承WebSecurityConfigurerAdapter,重写 configure(HttpSecurity http)
- @Configuration
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.formLogin(); // 表单登录
- http.authorizeRequests() //认证配置
- .anyRequest() // 任何请求
- .authenticated(); // 都需要身份验证
- }
- }
2.编写Controller
- @RestController
- public class helloController {
- @GetMapping("/hello")
- public String hello(){
- return "hello,springSecurity!";
- }
- }
3.运行项目后地址栏键入localhost:8080/hello.会自动进入登录界面:
默认的用户名:user ,密码在项目启动的时候会在控制台打印
4.登录后收到服务器的响应
SpringSecurity 本质是一个过滤器链。所有过滤器如下:
(1) WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。
(2) SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上 下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将 SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将 SecurityContextHolder 中的信息清除,例如在 Session 中维护一个用户的安全信息就是这个过滤器处理的。
(3) HeaderWriterFilter:用于将头信息加入响应中。
(4) CsrfFilter:用于处理跨站请求伪造。
(5)LogoutFilter:用于处理退出登录。
(6)UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中 获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码 时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的 usernameParameter 和 passwordParameter 两个参数的值进行修改。
(7)DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
(8)BasicAuthenticationFilter:检测和处理 http basic 认证。
(9)RequestCacheAwareFilter:用来处理请求的缓存。
(10)SecurityContextHolderAwareRequestFilter:主要是包装请求对象 request。 (11)AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在 Authentication 对象,如果不存在为其提供一个匿名 Authentication。
(12)SessionManagementFilter:管理 session 的过滤器
(13)ExceptionTranslationFilter:处理 AccessDeniedException 和 AuthenticationException 异常。
(14)FilterSecurityInterceptor:可以看做过滤器链的出口。
(15)RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的 remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。
Spring Security 采取过滤链实现认证与授权,只有当前过滤器通过,才能进入下一个过滤器:
绿色部分是认证过滤器,需要我们自己配置,可以配置多个认证过滤器。可以使用 SpringSecurity提供的认证过滤器,也可以自定义过滤器(例如短信验证)。都需要通过重写 configure(HttpSecurity http)来配置,没有配置不生效。下面重点介绍以下三个过滤器:
1.UsernamePasswordAuthenticationFilter 过滤器:该过滤器会拦截前端提交的 POST 方式的登录表单请求,并进行身份认证。
2.ExceptionTranslationFilter 过滤器:该过滤器不需要我们配置,对于前端提交的请求会 直接放行,捕获后续抛出的异常并进行处理(例如:权限访问限制)。
3. FilterSecurityInterceptor 过滤器:该过滤器是过滤器链的最后一个过滤器,根据资源权限配置来判断当前请求是否有权限访问对应的资源。如果访问受限会抛出相关异常,并由 ExceptionTranslationFilter 过滤器进行捕获和处理。
1. Spring Boot 项目启动后,SecurityFilterAutoConfiguration类会加载 DelegatingFilterProxyRegistrationBean注册过滤器,名字-springSecurityFilterChain。
2.DelegatingFilterProxyRegistrationBean 注册成功后,该过滤器就被加载到了注册器中。DelegatingFilterProxy类通过 springSecurityFilterChain的名字,得到了一个 FilterChainProxy过滤器,最终执行的是这个过滤器的 doFilter方法。
2. FilterChainProxy过滤器包含的属性:过滤器链-List<SecurityFilterChain> filterChains
它的doFilter方法中调用了doFilterInternal方法,里面就包含了所有过滤器(这些过滤器都被封装进了 SecurityFilterChain对象中)。
3.doFilterInternal方法中会调用 getFilters方法,会从过滤器链中拿出所有拦截器,创建一个虚拟的过滤器链,执行其doFilter方法。
认证流程是在 UsernamePasswordAuthenticationFilter 过滤器中处理。
1.当前端提交的是一个 POST 方式的登录表单请求,就会被该过滤器拦截,并进行身份认 证。该过滤器的 doFilter() 方法实现在其抽象父类AbstractAuthenticationProcessingFilter中。
1.1 首先判断是否是登录认证的请求,若是则放行,不是则进入下一步
1.2 调用子类UsernamePasswordAuthenticationFilter 中的attemptAuthentication进行认证。返回的authResult对象封装了认证信息。
1.2.1 判断是否是POST类型的提交
1.2.2 获取表单数据(username、password)
1.2.3 构造Authentication 对象,标记为未认证。返回的对象类型为UsernamePasswordAuthenticationToken,是Authentication 接口的实现类,该类有两个构造器:一个用于封装前端请求传入的未认证的用户信息(用户权限(null)、用户名、密码、标记(false)),一个用于封装认证成功后的用户信息(用户权限(集合)、UserDetails对象、密码、标记(true))
1.2.4 将请求中的一些属性信息设置到 Authentication 对象中,如remoteAddress、sessionID
1.2.5 调用providerManager类的authenticate方法进行认证。ProviderManager 是 AuthenticationManager 接口的实现类。(ProviderManager内部会维护一个 List列表,存放多种认证方式。每种认证方式对应着一个 AuthenticationProvider,根据传入的 Authentication 类型委托对应的 AuthenticationProvider 进行用户认证。)
1.2.5.1 获取Authentication 对象的类型
1.2.5.2 获取认证方式列表的迭代器
1.2.5.3 判断当前AuthenticationProvider是否适合当前 Authentication 对象
1.2.5.4 调用当前AuthenticationProvider的authenticate方法进行认证(关联UserDetailService,将创建的Authentication 对象中的信息与数据库中存的UserDetails对比)。
1.2.5.5 若认证成功,返回标记为已认证的Authentication 对象,将details信息拷贝到其中。调用 CredentialsContainer 接口定义的 eraseCredentials() 方法去除result中的敏感信息(密码),最后发布认证成功的事件。
1.2.5.6 若认证失败,交给其父类AuthenticationManager处理。如果还是不行,抛异常。
1.3 对session的策略进行处理(比如配置了session的最大并发数)
1.4 若认证成功,调用successfulAuthentication处理器;
1.4.1 将认证成功的 Authentication 对象封装进securityContext对象中,存入securityContextHolder(是对ThreadLocal的封装,创建securityContext)中。
1.4.2 处理remeberMe
1.4.3 发布认证成功事件
1.4.4 调用认证成功处理器-onAuthenticationSuccess
1.5 若认证失败,调用unsuccessfulAuthentication处理器。
1.5.1 清除该线程在securityContextHolder中的securityContext对象。
1.5.2 处理remeberMe
1.5.3 调用认证失败处理器-onAuthenticationFailure
1.ExceptionTranslationFilter 过滤器
1.1 对前端提交的请求直接放行
1.2 捕获后续的异常并进行处理(未认证、权限不足)
2.FilterSecurityInterceptor 过滤器
2.1 根据资源权限配置判断是否有权限访问资源,若无则抛异常。
2.1.1 获取当前 request 所对应的 ConfigAttribute,即权限信息
2.1.2 判断SecurityContext中有没有用户信息。如果没有则抛错,有则继续执行。
2.1.3 调用authenticateIfRequired方法校验是否通过认证(内部调用authenticationManager.authenticate())
2.1.4 调用AbstractSecurityInterceptor类的attemptAuthorization方法进行权限校验(注意跟认证过程中的attemptAuthorization不一样)
2.1.5 发布权限校验成功事件
2.1.6 创建新的SecurityContext和Authentication对象,更新用户信息。
2.2 通过SpringMVC的DispatcherServlet访问资源。
注:Spring Security 的过滤器链是配置在 SpringMVC 的核心组件 DispatcherServlet 运行之前。也就是说,请求通过 Spring Security 的所有过滤器, 不意味着能够正常访问资源,该请求还需要通过 SpringMVC 的拦截器链。
一般认证成功后的用户信息是通过 Session 在多个请求之间共享,那么 Spring Security 中是如何实现将已认证的用户信息对象 Authentication 与 Session 绑定的呢?
SecurityContextPersistenceFilter 过滤器的位置在所有过滤器的最前面,请求到来先进它,响应返回最后一个通过它,所以在该过滤器中处理已认证的用户信息对象 Authentication 与 Session 绑定。
认证成功的响应通过 SecurityContextPersistenceFilter 过滤器时,会从 SecurityContextHolder 中取出封装了已认证用户信息对象 Authentication 的 SecurityContext,放进 Session 中。
当请求再次到来时,请求首先经过该过滤器,该过滤器会判断当前请求的 Session 是否存有 SecurityContext 对象,如果有则将该对象取出再次放入 SecurityContextHolder 中,无则创建空的。之后该请求所在的线程获得认证用户信息,后续的资源访问不需要进行身份认证;
当响应再次返回时,该过滤器同样从 SecurityContextHolder 取出 SecurityContext 对象,放入 Session 中。