Luckylau's Blog

SpringBoot之SpringSecurity

什么是SpringSecurity

​ Spring Security是基于Filter实现的对系统的访问权限进行细粒度的控制,包括用户认证(Authentication)和用户授权(Authorization);认证是验证某个用户是否为系统中的合法主体,授权是验证某个用户是否有权限执行某个操作。众所周知,想要对对Web资源进行保护,最好的办法莫过于Filter,要想对方法调用进行保护,最好的办法莫过于AOP。所以springSecurity在我们进行用户认证以及授予权限的时候,通过各种各样的拦截器来控制权限的访问,从而实现安全。

SpringSecurity的启动加载流程

SpringSecurityConfigurer

@EnableWebSecurity ->WebSecurityConfiguration->setFilterChainProxySecurityConfigurer->springSecurityFilterChain->AbstractConfiguredSecurityBuilder.doBuild()。

WebSecurityConfiguration首先调用它setFilterChainProxySecurityConfigurer方法进行webSecurity的初始化;

objectPostProcessor用于构建webSecurity,另外的注解将我们定义的SpringSecurityConfigurer转化到webSecurityConfigurers中。

然后再调用springSecurityFilterChain进行webSecurity的配置

下面的关键开始了

接着调用org.springframework.security.config.annotation.AbstractSecurityBuilder#build方法,进入doBuild()方法

这时候的configurers是”class com.yrd.bpm.security.SpringSecurityConfigurer$$EnhancerBySpringCGLIB$$463f5179” -> “ size = 1”,

doBuild()方法有2个非常重要的方法:init()和performBuild()

首先是init(),只会进入第一个的for循环,调用

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

的init()方法,我们看一下getHttp()方法

gethttp()方法重要的事情做了2件,第一件是构建接口AuthenticationManager的实现,实际是实例化ProviderManager,第二件是对http初始化,执行if(!disableDefaults)内的代码

我们看到http有1个filter和10个configurers

继续向下执行,会执行configure方法,就是我们自定义SpringSecurityConfigurer类的configure方法

这时候http变成了

然后调用web.addSecurityFilterChainBuilder(http),就是把http放入securityFilterChainBuilders,为下面的performBuild()作准备

关键的就是for循环里面的securityFilterChains.add(securityFilterChainBuilder.build()),这个时候又进入doBuild(),处理上图中的http的11个configure。

在doBuild()中init()和configure(), init()对11个configure处理

其中org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer主要配置是否有securityContext,一般jwt的方式是配置stateless为true

configure()将configure转化为filter,

最后performBuilder(),将filter排序后构建DefaultSecurityFilterChain

org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer

一个请求经过SpringSecurity的处理流程

经过上面的流程之后会生成如下13个过滤链

WebAsyncManagerIntegrationFilter

将Security上下文与Spring Web中用于处理异步请求映射的 WebAsyncManager 进行集成。

SecurityContextPersistenceFilter

​ 在每次请求处理之前将该请求相关的安全上下文信息加载到SecurityContextHolder中,然后在该次请求处理完成之后,将SecurityContextHolder中的信息清除,同时将关于这次请求的信息存储到一个SecurityContextRepository中。这里要注意的是SecurityContextRepository的实现有2个,HttpSessionSecurityContextRepository和NullSecurityContextRepository,当你用token验证时候,不用session管理会话时,即stateless为true,会使用NullSecurityContextRepository,这时候SecurityContextHolder里面存取都是空的。

HeaderWriterFilter

用于将头信息加入响应中

LogoutFilter

用于处理退出登录

执行我定义的过滤链

UsernamePasswordAuthenticationFilter

​ 用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自“/login”的请求。

​ 从表单中获取用户名和密码时,默认使用的表单name值为“username”和“password”,这两个值可以通过设置这个过滤器的usernameParameter 和 passwordParameter 两个参数的值进行修改。

ConcurrentSessionFilter

如果session没过期, 就会更新session里的”last update” date/time;

如果session过期, 就会调用logout handlers(一般是LogoutFilter)去销毁session, 然后跳转到expiredUrl;

RequestCacheAwareFilter

用于用户登录成功后,重新恢复因为登录被打断的请求;

SecurityContextHolderAwareRequestFilter

对request包装的目的主要是实现servlet api的一些接口方法isUserInRole、getRemoteUser

AnonymousAuthenticationFilter

如果没有任何认证程序机制更新SecurityContextHolder,一个匿名的对象将存放到这里

SessionManagementFilter

当用户重启浏览器, 然后使用remember-me授权成功后, 再直接访问授权后的url. 这时, session里没有了”SPRING_SECURITY_CONTEXT”属性. 那么这个请求会被此过滤器拦截. 这时得到的authentication是RemembermeAuthentication的实例。

当session因过期而被容器自动销毁时, 若用户继续发出请求, 这个请求会放在一个新的session里, 新session里没有设置”SPRING_SECURITY_CONTEXT”属性, 那么这个请求也会被这个过滤器拦截. 这时得到的authentication是AnonymousAuthentication的实例。

ExceptionTranslationFilter

此过滤器的作用是处理中FilterSecurityInterceptor抛出的异常,然后将请求重定向到对应页面,或返回对应的响应错误代码。

FilterSecurityInterceptor

功能一:如果用户尚未登陆,则抛出AuthenticationCredentialsNotFoundException“尚未认证异常”。

功能二:如果用户已登录,但是没有访问当前资源的权限,则抛出AccessDeniedException“拒绝访问异常”。

功能三:如果用户已登录,也具有访问当前资源的权限,则放行。

SpringSecurity的使用

参考github:https://github.com/Luckylau/Spring-Learning.git

参考文章

https://blog.csdn.net/liushangzaibeijing/article/details/81220610

Luckylau wechat
如果对您有价值,看官可以打赏的!