上次有小伙伴建议,源码分析太枯燥了,要是可以结合设计模式一块儿来,这样更有助于你们理解 Spring Security 源码,同时还能复习一波设计模式。java
所以松哥今天就试着整一篇,和你们来聊一聊 Spring Security 中涉及到的设计模式,不过 Spring Security 中涉及到的设计模式仍是很是多的,松哥这里讲几个,剩下的欢迎小伙伴们留言补充。算法
Template Pattern(模板方法模式)是一个抽象类公开定义了执行它的方法的模板。它的子类能够按须要重写方法实现,但调用将以抽象类中定义的方式进行,这是一种行为型模式。
模板方法方式优势以下:spring
缺点以下:数据库
介绍完模板方法模式,你们可能大概猜到了 Spring Security 中哪些地方用到模板方法模式了。设计模式
我举几个简单的例子。session
第一个例子是 AbstractUserDetailsAuthenticationProvider 类的设计。你们都知道这个类是用来作验证的,认证的逻辑在这个方法中都定义好了,可是该类却定义了两个抽象方法:并发
这两个抽象方法是在 DaoAuthenticationProvider 中实现的。DaoAuthenticationProvider 的实现就是从数据库中加载用户,默认检验登陆凭证也都是验证密码。app
若是你的数据源来自其余地方,或者登陆凭证不是密码,那么自定义类继承自 AbstractUserDetailsAuthenticationProvider 并重写它里边的这两个方法便可。框架
Chain of Responsibility Pattern(责任链模式) ,在这种模式中,一般每一个接收者都包含对另外一个接收者的引用,若是一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。在这个过程当中,客户只须要将请求发送到责任链上便可,无须关心请求的处理细节和请求的传递过程,因此责任链将请求的发送者和请求的处理者解耦了。
责任链模式优势以下:ide
缺点以下:
很明显,Spring Security 中的过滤器链就是一种责任链模式。一个请求到达后,被过滤器链中的过滤器逐个进行处理,过滤器链中的过滤器每一个都具备不一样的职能而且互不相扰,咱们还能够经过 HttpSecurity 来动态配置过滤器链中的过滤器(即添加/删除过滤器链中的过滤器)。
具体的代码在 FilterChainProxy$VirtualFilterChain 中,以下:
那么接下来咱们就来看看 VirtualFilterChain:
private static class VirtualFilterChain implements FilterChain { private final FilterChain originalChain; private final List<Filter> additionalFilters; private final FirewalledRequest firewalledRequest; private final int size; private int currentPosition = 0; private VirtualFilterChain(FirewalledRequest firewalledRequest, FilterChain chain, List<Filter> additionalFilters) { this.originalChain = chain; this.additionalFilters = additionalFilters; this.size = additionalFilters.size(); this.firewalledRequest = firewalledRequest; } @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (currentPosition == size) { if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " reached end of additional filter chain; proceeding with original chain"); } // Deactivate path stripping as we exit the security filter chain this.firewalledRequest.reset(); originalChain.doFilter(request, response); } else { currentPosition++; Filter nextFilter = additionalFilters.get(currentPosition - 1); if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " at position " + currentPosition + " of " + size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'"); } nextFilter.doFilter(request, response, this); } } }
currentPosition == size
,表示过滤器链已经执行完毕,此时经过调用 originalChain.doFilter 进入到原生过滤链方法中,同时也退出了 Spring Security 过滤器链。不然就从 additionalFilters 取出 Spring Security 过滤器链中的一个个过滤器,挨个调用 doFilter 方法。nextFilter.doFilter 就是过滤器链挨个往下走。关于 FilterChainProxy 的介绍,参见:深刻理解 FilterChainProxy【源码篇】
Strategy Pattern(策略模式),它定义了一系列算法,将每个算法封装起来,并让它们能够相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
策略模式的优势:
策略模式的缺点:
Spring Security 中使用策略模式的地方也有好几个。
第一个就是用户登陆信息存储。
在 SecurityContextHolder 中定义登陆用户信息存储的方法,就定义了三种不一样的策略:
public class SecurityContextHolder { // ~ Static fields/initializers // ===================================================================================== public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL"; public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL"; public static final String MODE_GLOBAL = "MODE_GLOBAL"; public static final String SYSTEM_PROPERTY = "spring.security.strategy"; private static String strategyName = System.getProperty(SYSTEM_PROPERTY); private static SecurityContextHolderStrategy strategy; }
用户能够自行选择使用哪种策略!具体参见:在 Spring Security 中,我就想从子线程获取用户登陆信息,怎么办?
还有一个就是 session 并发管理。
在 AbstractAuthenticationProcessingFilter#doFilter 方法中,有以下代码:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //省略 sessionStrategy.onAuthentication(authResult, request, response); //省略 }
这就是一种策略模式。
Session 并发管理能够参考:
固然,这样的例子还有不少,我就不一一列举了。
Proxy Pattern(代理模式) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用,它是一种对象结构型模式。
代理模式的优势:
缺点:
代理模式在 Spring Security 中最重要的应用就是 Spring Security 过滤器连接入 Web Filter 的过程,使用了 Spring 提供的 DelegatingFilterProxy,这就是一个典型的代理模式:
public class DelegatingFilterProxy extends GenericFilterBean { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Lazily initialize the delegate if necessary. Filter delegateToUse = this.delegate; if (delegateToUse == null) { synchronized (this.delegateMonitor) { delegateToUse = this.delegate; if (delegateToUse == null) { WebApplicationContext wac = findWebApplicationContext(); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: " + "no ContextLoaderListener or DispatcherServlet registered?"); } delegateToUse = initDelegate(wac); } this.delegate = delegateToUse; } } // Let the delegate perform the actual doFilter operation. invokeDelegate(delegateToUse, request, response, filterChain); } }
固然还有其余不少地方也用到代理模式,我就不一一列举了,欢迎小伙伴们留言补充。
Adapter Pattern(适配器模式),你们平时用的手机充电器学名叫作电源适配器,它的做用是把 220V 的电压转为手机可用的 5V 电压。因此适配器模式其实也是相似做用,将一个接口转换成客户但愿的另外一个接口,适配器模式使接口不兼容的类能够一块儿工做。适配器模式又分为类适配器模式、对象适配器模式以及接口适配器模式。
适配器模式的优势:
缺点:
Spring Security 中的适配器模式也是很是多的,例如咱们最为常见的 WebSecurityConfigurerAdapter,该类让两个本来不相关的 WebSecurity 和 HttpSecurity 可以在一块儿工做。
具体参见:[深刻理解 WebSecurityConfigurerAdapter【源码篇】]()
Builder Pattern(建造者模式)是将一个复杂对象的构建与它的表示分离,使得一样的构建过程能够建立不一样的对象出来,用户只须要指定复杂对象的类型和内容就能够构建对象,而不须要知道内部的具体构建细节。
建造者模式优势:
缺点:
Spring Security 中对于建造者模式的使用也是很是多,例如典型的 AuthenticationManagerBuilder,它想要建造的对象是 AuthenticationManager,对应的建造方法则是 build。通常建造者模式中建造者类命名以 builder 结尾,而建造方法命名为 build()。
关于 AuthenticationManagerBuilder,参见:深刻理解 AuthenticationManagerBuilder 【源码篇】 一文。
Observer(观察者模式)指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都获得通知并自动更新,观察者模式也称为发布-订阅模式、模型-视图模式,它是对象行为型模式。
观察者模式优势:
缺点:
在 Spring 框架中,观察者模式用于实现 ApplicationContext 的事件处理功能。Spring 为咱们提供了 ApplicationEvent 类和 ApplicationListener 接口来启用事件处理。Spring 应用程序中的任何 Bean 实现 ApplicationListener 接口,都会接收到 ApplicationEvent 做为事件发布者推送的消息。在这里,事件发布者是主题(Subject) 和实现 ApplicationListener 的 Bean 的观察者(Observer)。
具体到 Spring Security 中,如登陆成功事件发布,session 销毁事件等等,都算是观察者模式。
例如 AbstractAuthenticationProcessingFilter#successfulAuthentication 方法:
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (logger.isDebugEnabled()) { logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult); } SecurityContextHolder.getContext().setAuthentication(authResult); rememberMeServices.loginSuccess(request, response, authResult); // Fire event if (this.eventPublisher != null) { eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( authResult, this.getClass())); } successHandler.onAuthenticationSuccess(request, response, authResult); }
相似还有不少,如 session 销毁事件等(参见Spring Security 自动踢掉前一个登陆用户,一个配置搞定!),我这里就不一一列举了。
Decorator(装饰模式)是指在不改变现有对象结构的状况下,动态地给该对象增长一些额外功能的模式。
装饰模式的优势:
缺点:
Spring Security 中对于装饰模式也有许多应用。最典型的就是一个请求在经过过滤器链的时候会不停的变,会不停的调整它的功能,经过装饰模式设计出了请求的许多类,例如:
等等,相似的不少,我就不一一赘述了。
松哥的 Spring Security 还在持续连载中,将来连载完了还会总结出更多的设计模式,这里先列出来八个和小伙伴们分享,若是小伙伴们有本身的看法,也欢迎留言补充。