写在后面
上回咱们讲了spring security整合spring springmvc的流程,并且晓得了spring security是通过过滤器链来进行认证受权操作的。明天咱们来剖析一下springsecurity过滤器链的加载流程。读者在浏览本文时能够边浏览边跟着操作,这样子会了解的更分明一些。
Spring Security过滤器链
spring security的过滤器十分多,这里简略介绍几个罕用的过滤器。
spring security常过滤器链介绍
org.springframework.security.web.context.SecurityContextPersistenceFilter
次要是应用SecurityContextRepository在session中保留或更新一个SecurityContext域对象(相当于一个容器),并将SecurityContext给当前的过滤器应用,来为后续filter建设所需的上下文。SecurityContext中存储了以后用户的认证以及权限信息。 其余的过滤器都须要依赖于它。在 Spring Security 中,尽管平安上下文信息被存储于 Session 中,但咱们在理论应用中不应该间接操作 Session,而该当应用 SecurityContextHolder。
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
这个过滤器用于集成SecurityContext到Spring异步执行机制的WebAsyncManager 中。如果想要与spring集成,就必须要应用此过滤器链。
org.springframework.security.web.csrf.CsrfFilter
csrf又称跨域申请伪造,SpringSecurity会对所有post申请验证是否蕴含系统生成的csrf的token信息,如果不蕴含,则报错。起到避免csrf攻打的成果。
题外话:csrf攻打
CSRF(Cross-site request forgery)跨站申请伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻打网站发送跨站申请。利用受害者在被攻打网站曾经获取的注册凭证,绕过后盾的用户验证,达到假冒用户对被攻打的网站执行某项操作的目标。
一个例子:
比方你登陆了银行网站A之后,浏览器cookie中将保留你的登录信息。同时你没有没有登记登录的状况下又用同一浏览器拜访了视频网站B,假如视频网站B中含有csrf病毒,其将获取你的cookie内容,拿到你银行网站A的登录信息,就能够进行不可形容之操作了。
而这个CsrfFilter通过签发token的形式进行拜访验证,如果token不是本网站签发的或者拜访申请中不带有这个token则回绝拜访。
org.springframework.security.web.authentication.logout.LogoutFilter
匹配URL为/logout的申请,实现用户退出,革除认证信息。
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST申请。
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
由此过滤器能够生产一个默认的退出登录页面
org.springframework.security.web.authentication.www.BasicAuthenticationFilter
此过滤器会主动解析HTTP申请中头部名字为Authentication,且以Basic结尾的头信息。
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
通过HttpSessionRequestCache外部保护了一个RequestCache,用于缓存HttpServletRequest
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
针对ServletRequest进行了一次包装,使得request具备更加丰盛的API
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
当SecurityContextHolder中认证信息为空,则会创立一个匿名用户存入到SecurityContextHolder中。
spring security为了兼容未登录的拜访,也走了一套认证流程,只不过是一个匿名的身份(游客)。
org.springframework.security.web.session.SessionManagementFilter
SecurityContextRepository限度同一用户开启多个会话的数量
org.springframework.security.web.access.ExceptionTranslationFilter
异样转换过滤器位于整个springSecurityFilterChain的前方,用来转换整个链路中呈现的异样
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
获取所配置资源拜访的受权信息,依据SecurityContextHolder中存储的用户信息来决定其是否有权限。
spring security加载流程
在web.xml中,咱们配置了一个名字为“springSecurityFilterChain”的过滤器org.springframework.web.filter.DelegatingFilterProxy。以下是局部源码。
public class DelegatingFilterProxy extends GenericFilterBean {
@Nullable
private String contextAttribute;
@Nullable
private WebApplicationContext webApplicationContext;
@Nullable
private String targetBeanName;
private boolean targetFilterLifecycle;
/** 真正加载的过滤器*/
@Nullable
private volatile Filter delegate;
private final Object delegateMonitor;
protected void initFilterBean() throws ServletException {
synchronized(this.delegateMonitor) {
if (this.delegate == null) {
if (this.targetBeanName == null) {
this.targetBeanName = this.getFilterName();
}
WebApplicationContext wac = this.findWebApplicationContext();
if (wac != null) {
this.delegate = this.initDelegate(wac);
}
}
}
}
/** 过滤器的入口 */
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized(this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = this.findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
}
/** 在做完一系列判断之后,真正要做的就是这一步,初始化delegate*/
delegateToUse = this.initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
this.invokeDelegate(delegateToUse, request, response, filterChain);
}
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = this.getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
if (this.isTargetFilterLifecycle()) {
delegate.init(this.getFilterConfig());
}
return delegate;
}
protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
}
接下来咱们在initDelegate办法上打个断点调试一下
最终咱们发现,通过initDelegate办法给delegate初始化,失去一个FilterChainProxy对象。接下来咱们进入FilterChainProxy这个类看看
public class FilterChainProxy extends GenericFilterBean {
。。。。
}
能够发现这也是一个过滤器,那么咱们找dofilter办法。
接下来咱们在doFilterInternal外面打个断点试试,看看能不能失去过滤器链
咱们在这一步失去了一个List<Filter>,点开能够发现正好是前文讲到的一些罕用的过滤器。
咱们再进入到getFilters中看看这些过滤器链在哪
private List<Filter> getFilters(HttpServletRequest request) {
Iterator var2 = this.filterChains.iterator();
SecurityFilterChain chain;
do {
if (!var2.hasNext()) {
return null;
}
chain = (SecurityFilterChain)var2.next();
} while(!chain.matches(request));
return chain.getFilters();
}
从该办法咱们晓得过滤器链来自于SecurityFilterChain的getFilters办法,接下来,咱们看看这个类
public interface SecurityFilterChain {
boolean matches(HttpServletRequest var1);
List<Filter> getFilters();
}
能够发现这是一个接口,在idea中ctrl+H能够发现它只有一个实现类,那就是DefaultSecurityFilterChain
到这一步,咱们能够发现,springSecurity的过滤器链存在于SecurityFilterChain中(springSecurity的过滤器链由SecurityFilterChain负责封装),它只有一个实现类那就是DefaultSecurityFilterChain。也就是说其过滤器链由SecurityFilterChain封装。
至此,springSecurity的过滤器链加载流程咱们就说完了。
发表回复