乐趣区

关于java:盘点-Spring-Security-框架中的八大经典设计模式

1. 模板办法模式

Template Pattern(模板办法模式) 是一个抽象类公开定义了执行它的办法的模板。它的子类能够按须要重写办法实现,但调用将以抽象类中定义的形式进行,这是一种行为型模式。

模板办法形式长处如下:

  • 在父类中提取了公共的局部代码,便于代码复用和扩大。
  • 局部办法是由子类实现的,子类能够通过扩大形式减少相应的性能,合乎开闭准则。

毛病如下:

  • 对每个不同的实现都须要定义一个子类,导致类的个数减少,零碎更加简单,设计也更加形象。
  • 父类中的形象办法由子类实现,子类执行的后果会影响父类的后果,减少了代码了解难度。

介绍完模板办法模式,大家可能大略猜到了 Spring Security 中哪些地方用到模板办法模式了。

我举几个简略的例子。

第一个例子是 AbstractUserDetailsAuthenticationProvider 类的设计。大家都晓得这个类是用来做验证的,认证的逻辑在这个办法中都定义好了,然而该类却定义了两个形象办法:

  • retrieveUser 该办法用户从数据源中获取用户对象。
  • additionalAuthenticationChecks 该办法用来做额定的校验(登录凭证的校验)

这两个形象办法是在 DaoAuthenticationProvider 中实现的。DaoAuthenticationProvider 的实现就是从数据库中加载用户,默认测验登录凭证也都是验证明码。

如果你的数据源来自其余中央,或者登录凭证不是明码,那么自定义类继承自 AbstractUserDetailsAuthenticationProvider 并重写它里边的这两个办法即可。

2. 责任链模式

Chain of Responsibility Pattern(责任链模式),在这种模式中,通常每个接收者都蕴含对另一个接收者的援用,如果一个对象不能解决该申请,那么它会把雷同的申请传给下一个接收者,依此类推。在这个过程中,客户只须要将申请发送到责任链上即可,毋庸关怀申请的解决细节和申请的传递过程,所以责任链将申请的发送者和申请的解决者解耦了。

责任链模式长处如下:

  • 升高对象之间的耦合度。
  • 加强了零碎的可扩展性。
  • 当工作流程发生变化,能够动静地扭转链内的成员或者调动它们的秩序。
  • 简化了对象之间的连贯,每个对象只需放弃一个指向其后继者的援用,不需放弃其余所有解决者的援用。
  • 责任分担,每个类只须要解决本人该解决的工作,合乎类的繁多职责准则。

毛病如下:

  • 对比拟长的职责链,申请的解决可能波及多个解决对象,零碎性能将受到肯定影响。
  • 职责链建设的合理性要靠客户端来保障,减少了客户端的复杂性。

很显著,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);
        }
    }
}
  1. VirtualFilterChain 类中首先申明了 5 个全局属性,originalChain 示意原生的过滤器链,也就是 Web Filter;additionalFilters 示意 Spring Security 中的过滤器链;firewalledRequest 示意以后申请;size 示意过滤器链中过滤器的个数;currentPosition 则是过滤器链遍历时候的下标。
  2. doFilter 办法就是 Spring Security 中过滤器挨个执行的过程,如果 currentPosition == size,示意过滤器链曾经执行结束,此时通过调用 originalChain.doFilter 进入到原生过滤链办法中,同时也退出了 Spring Security 过滤器链。否则就从 additionalFilters 取出 Spring Security 过滤器链中的一个个过滤器,挨个调用 doFilter 办法。nextFilter.doFilter 就是过滤器链挨个往下走。

对于 FilterChainProxy 的介绍,参见:深刻了解 FilterChainProxy【源码篇】

3. 策略模式

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 并发治理能够参考:

  • 什么是会话固定攻打?Spring Boot 中要如何进攻会话固定攻打?
  • 集群化部署,Spring Security 要如何解决 session 共享?

当然,这样的例子还有很多,我就不一一列举了。

4. 代理模式

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);
    }
}

当然还有其余很多中央也用到代理模式,我就不一一列举了,欢送小伙伴们留言补充。

5. 适配器模式

Adapter Pattern(适配器模式),大家平时用的手机充电器学名叫做电源适配器,它的作用是把 220V 的电压转为手机可用的 5V 电压。所以适配器模式其实也是相似作用,将一个接口转换成客户心愿的另一个接口,适配器模式使接口不兼容的类能够一起工作。适配器模式又分为类适配器模式、对象适配器模式以及接口适配器模式。

适配器模式的长处:

  • 解耦,通过引入一个适配器类来重用现有的适配者类,而无须批改原有代码。
  • 减少了类的透明性和复用性。
  • 具备较好的灵活性和扩展性都。

毛病:

  • 因为 Java 不反对多重继承,一次最多只能适配一个适配者类,而且指标抽象类只能为抽象类,不能为具体类,其应用有肯定的局限性。

Spring Security 中的适配器模式也是十分多的,例如咱们最为常见的 WebSecurityConfigurerAdapter,该类让两个本来不相干的 WebSecurity 和 HttpSecurity 可能在一起工作。

具体参见:[深刻了解 WebSecurityConfigurerAdapter【源码篇】]()

6. 建造者模式

Builder Pattern(建造者模式) 是将一个简单对象的构建与它的示意拆散,使得同样的构建过程能够创立不同的对象进去,用户只须要指定简单对象的类型和内容就能够构建对象,而不须要晓得外部的具体构建细节。

建造者模式长处:

  • 将产品自身与产品的创立过程解耦,使得雷同的创立过程能够创立不同的产品对象,而客户端不须要晓得产品外部细节。
  • 每一个产品对应一个建造者,用户应用不同的建造者能够创立不同的产品,建造者自身能够轻松批改或者增加。
  • 能够更加精密地管制产品的创立过程。

毛病:

  • 创立的产品须要有肯定的相似性,如果差别过大,则不适宜建造者模式。
  • 产品自身的复杂度会进步建造者的复杂度。

Spring Security 中对于建造者模式的应用也是十分多,例如典型的 AuthenticationManagerBuilder,它想要建造的对象是 AuthenticationManager,对应的建造办法则是 build。个别建造者模式中建造者类命名以 builder 结尾,而建造办法命名为 build()。

对于 AuthenticationManagerBuilder,参见:深刻了解 AuthenticationManagerBuilder【源码篇】一文。

7. 观察者模式

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 主动踢掉前一个登录用户,一个配置搞定!),我这里就不一一列举了。

8. 装璜模式

Decorator(装璜模式) 是指在不扭转现有对象构造的状况下,动静地给该对象减少一些额定性能的模式。

装璜模式的长处:

  • 能够灵便的扩大一个类的性能。

毛病:

  • 减少了许多子类,使程序变得很简单。

Spring Security 中对于装璜模式也有许多利用。最典型的就是一个申请在通过过滤器链的时候会不停的变,会不停的调整它的性能,通过装璜模式设计出了申请的许多类,例如:

  • HeaderWriterRequest
  • FirewalledRequest
  • StrictHttpFirewall
  • SaveToSessionRequestWrapper

等等,相似的很多,我就不一一赘述了。

退出移动版