1. Template method pattern

The template method pattern is an abstract class that overtly defines a template for executing its methods. Its subclasses can override the method implementation as needed, but the invocation will be made in the same way as defined in the abstract class, which is a behavioral pattern.

The advantages of the Template method are as follows.

  • The public part of the code is extracted in the parent class, which is easy to reuse and extend the code.
  • Some of the methods are implemented by subclasses, and subclasses can add the corresponding functions by extending the approach, which is in line with the open-close principle.

Disadvantages are as follows.

  • A subclass needs to be defined for each different implementation, leading to an increase in the number of classes, a more complex system, and a more abstract design.
  • The abstract methods in the parent class are implemented by the subclasses, and the result of the subclass execution will affect the result of the parent class, which makes the code more difficult to understand.

After introducing the template method pattern, you may have guessed where the template method pattern is used in Spring Security.

Let me give you a few simple examples.

The first example is the design of the AbstractUserDetailsAuthenticationProvider class. We all know that this class is used for authentication, and the logic of authentication is defined in this method, but the class defines two abstract methods.

  • retrieveUser: This method is used to retrieve the user object from the data source.
  • additionalAuthenticationChecks: This method is used to do additional checks (checks of login credentials)

These two abstract methods are implemented in DaoAuthenticationProvider, which loads the user from the database and checks the login credentials for the password by default.

If your data source is from somewhere else, or if the login credentials are not a password, then a custom class inherits from AbstractUserDetailsAuthenticationProvider and overrides the two methods in it.

2. Chain of Responsibility Pattern

Chain of Responsibility Pattern, in this pattern, usually each receiver contains a reference to another receiver, if an object can not handle the request, then it will pass the same request to the next receiver, and so on. In this process, the client only needs to send the request to the chain of responsibility and does not need to care about the details of the request processing and the request delivery process, so the chain of responsibility decouples the sender of the request from the processor of the request.

The advantages of the Chain of Responsibility pattern are as follows.

  • Reduces the coupling between objects.
  • Enhanced scalability of the system.
  • When the workflow changes, the members of the chain can be dynamically changed or their order can be moved.
  • Simplifies connections between objects, each object only needs to maintain a reference to its successor and not to all other handlers.
  • Responsibility is shared, each class only needs to handle the work it is supposed to handle, in line with the single responsibility principle for classes.

The disadvantages are as follows.

  • For longer responsibility chains, the processing of requests may involve more than one processing object, and the system performance will be affected to some extent.
  • The reasonableness of the responsibility chain establishment depends on the client to ensure, which increases the complexity of the client.

Obviously, the filter chain in Spring Security is a chain-of-responsibility model. A request arrives and is processed one by one by the filters in the filter chain, each filter in the filter chain has a different function and does not interfere with each other, we can also dynamically configure the filters in the filter chain through HttpSecurity (i.e. add/remove filters in the filter chain).

The specific code is in FilterChainProxy$VirtualFilterChain as follows.

So let’s look at VirtualFilterChain next.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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. The VirtualFilterChain class first declares 5 global properties, originalChain indicates the original filter chain, that is, Web Filter; additionalFilters indicates the filter chain in Spring Security; firedRequest indicates the current request; size indicates the number of filters in the filter chain; and currentPosition is the index of the filter chain when it is traversed.
  2. doFilter method is the process of executing filters one by one in Spring Security. If currentPosition == size, it means that the filter chain has been executed, and then the originalChain.doFilter is called to enter the original filter chain method and exit the Spring Security filter chain. Otherwise, we take each filter in the Spring Security filter chain from the additionalFilters and call the doFilter method one by one. nextFilter.doFilter is executed one by one down the filter chain.

3. Strategy Pattern

Strategy Pattern, which defines a set of algorithms, encapsulates each algorithm, and makes them interchangeable. A strategy pattern allows an algorithm to change independently of the client using it, also known as a policy pattern.

Advantages of Strategy Pattern.

  • The policy model provides perfect support for the “open-close principle”, which allows users to choose specific policies without modifying the original system, or to flexibly extend new policies.
  • The policy model provides a way to manage related policies.
  • The policy model provides a way to replace inheritance relationships.
  • Using the policy pattern avoids the need for multiple conditional transfer statements.

Disadvantages of the strategy pattern.

  • The client must know all the policy classes and decide for itself which one to use.
  • The policy pattern will result in the creation of many policy classes (the number of objects can be reduced to some extent by using the Flyweight Pattern).

There are also several places where the policy pattern is used in Spring Security.

The first one is the user login information storage.

Three different strategies are defined in the SecurityContextHolder regarding the storage of logged-in user information.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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;
}

Another one is session concurrency management.

In the AbstractAuthenticationProcessingFilter#doFilter method, there is the following code.

1
2
3
4
5
6
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
  throws IOException, ServletException {
 //...
  sessionStrategy.onAuthentication(authResult, request, response);
 //...
}

This is a strategy pattern.

Of course, there are many more such examples, so I won’t list them all.

4. Proxy Pattern

Proxy Pattern: Provide a proxy to an object and control the reference to the original object by the proxy object, it is an object structured pattern.

Advantages of Proxy Pattern.

  • Reduces the coupling of the system to some extent.
  • Proxy object can extend the functionality of the target object.
  • Proxy object can protect the target object.

Disadvantages.

  • Adds a proxy between the client and the real object, which may cause slower processing of requests.
  • Increases system complexity.

The most important application of the proxy pattern in Spring Security is the linking of Spring Security filters into the Web Filter, using the DelegatingFilterProxy provided by Spring, which is a typical proxy pattern.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

Adapter Pattern, the scientific name of the phone charger you usually use is called power adapter, its role is to convert the 220V voltage to the 5V voltage available to the phone. So the adapter pattern is actually a similar role, converting an interface into another interface that the customer wants, the adapter pattern makes the interface incompatible classes can work together. The adapter pattern is further divided into the class adapter pattern, the object adapter pattern, and the interface adapter pattern.

Advantages of the adapter pattern.

  • Decoupling, by introducing an adapter class to reuse existing adaptor classes without modifying the original code.
  • Increased class transparency and reusability.
  • Better flexibility and extensibility.

Disadvantages.

  • Since Java does not support multiple inheritance, only at most one adapter class can be adapted at a time, and the target abstract class can only be an abstract class, not a concrete class, its use has some limitations.

Spring Security in the adapter pattern is also very much, such as our most common WebSecurityConfigurerAdapter, the class allows two originally unrelated WebSecurity and HttpSecurity can work together.

6. Builder Pattern

Builder Pattern is to separate the construction of a complex object from its representation, so that the same construction process can create different objects out of it, and the user only needs to specify the type and content of the complex object to build the object, without needing to know the specific construction details inside.

Builders pattern advantages.

  • Decouples the product itself from the product creation process, so that the same creation process can create different product objects without the client needing to know the internal details of the product.
  • Each product corresponds to a builder, so users can create different products using different builders, and the builders themselves can be easily modified or added.
  • The product creation process can be controlled in a more fine-grained way.

Disadvantages.

  • The products created need to have some similarity, and if the differences are too great, the builder pattern is not suitable.
  • The complexity of the product itself will increase the complexity of the builder.

Spring Security also has a lot of use for the builder pattern, such as the typical AuthenticationManagerBuilder, which wants to build the object AuthenticationManager, and the corresponding build method is build. The builder class is generally named with the builder ending, and the build method is named build().

7. Observer pattern

Observer (Observer pattern) refers to the existence of a one-to-many dependency relationship between multiple objects, when the state of an object changes, all objects that depend on it are notified and automatically updated, Observer pattern is also known as publish-subscribe pattern, model-view pattern, it is object behavior type pattern.

Observer pattern advantages.

  • Reduces the coupling between the target and the observer, and the two are abstractly coupled.

Disadvantages.

  • The dependency between the target and the observer is not completely lifted, and there is a possibility of circular references.
  • When there are many observer objects, the program execution efficiency is reduced.

In the Spring framework, the observer pattern is used to implement the event handling capabilities of the ApplicationContext, and Spring provides us with the ApplicationEvent class and the ApplicationListener interface to enable event handling. any bean in a Spring application that implements the ApplicationListener interface will receive an ApplicationEvent message pushed as an event publisher. In this case, the event publisher is the Subject and the Observer of the bean that implements the ApplicationListener.

For example, the AbstractAuthenticationProcessingFilter#successfulAuthentication method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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);
}

Similarly there are many more, such as session destruction events, etc., which will not be listed here.

8. Decorator pattern

Decorator (Decorator pattern) is a pattern that dynamically adds some additional functionality to an existing object without changing the structure of that object.

Advantages of the Decorator pattern.

  • Flexibility to extend the functionality of a class.

Disadvantages.

  • Adds many subclasses, making the program complex.

Spring Security also has many applications for the decoration pattern. The most typical is that a request will keep changing as it passes through the filter chain, and will keep adjusting its function. Many classes of requests are designed through the decoration pattern, for example

  • HeaderWriterRequest
  • FirewalledRequest
  • StrictHttpFirewall
  • SaveToSessionRequestWrapper

And so on, there are many similar ones, so I won’t go into them all.

Refference: https://mp.weixin.qq.com/s/xZrsy7bp8xN3EJblz7TLHw