共计 5479 个字符,预计需要花费 14 分钟才能阅读完成。
写在后面
上回咱们讲了 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 的过滤器链加载流程咱们就说完了。