magical! The new objects can also be managed by the Spring container!

Posted by Dynamis on Tue, 23 Jun 2020 06:35:34 +0200

It's reasonable to say that the objects from new have nothing to do with containers, but in the Spring Security framework, there are many new objects that can also be managed by containers, so how does it do it?

Today, I'd like to talk with you about a little cold topic. What is the use of ObjectPostProcessor in Spring Security?

This is the 32nd article in the Spring Security series. Reading the previous articles will help you better understand this article:

  1. Dig a big hole and start Spring Security!
  2. Song Ge takes you to Spring Security by hand. Don't ask how to decrypt the password again
  3. How to customize form login in Spring Security
  4. Spring Security does front and back separation, let's not do page Jump! All JSON interactions
  5. The authorization operation in Spring Security is so simple
  6. How does Spring Security store user data in the database?
  7. Spring Security+Spring Data Jpa join hands, security management is only simpler!
  8. Spring Boot + Spring Security for automatic login
  9. How to control security risks when Spring Boot automatically logs in?
  10. In micro service projects, what is the advantage of Spring Security over Shiro?
  11. Two ways to customize the authentication logic of spring security (advanced play method)
  12. How to quickly view the IP address and other information of the login user in Spring Security?
  13. Spring Security automatically kicks out the previous login user, one configuration is done!
  14. Spring Boot + Vue front and back end separation project, how to kick off the logged in users?
  15. Spring Security comes with its own firewall! You don't know how secure your system is!
  16. What is session fixation attack? How to prevent session fixation attack in Spring Boot?
  17. How does Spring Security handle session sharing in a clustered deployment?
  18. SongGe teaches you how to defend against CSRF attacks in SpringBoot! so easy!
  19. To learn thoroughly! Analysis of CSRF defense source code in Spring Security
  20. Two ways of password encryption in Spring Boot!
  21. How to learn Spring Security? Why do we have to learn systematically?
  22. There are two resource release strategies of Spring Security. Don't use them wrong!
  23. SongGe teaches you how to start Spring Boot + CAS single sign on
  24. Spring Boot implements the third solution of single sign on!
  25. Spring Boot+CAS single sign on, how to connect the database?
  26. Spring Boot+CAS default login page is too ugly. What should I do?
  27. How to carry Token in request header when Swagger is used to test interface?
  28. Summary of three cross domain scenarios in Spring Boot
  29. How to implement HTTP authentication in Spring Boot?
  30. Four ways of authority control in Spring Security
  31. Multiple encryption schemes coexist in Spring Security, which is a powerful tool for the integration of old and dilapidated systems!

If you have studied the micro personnel project of SongGe, you will find that there is a line of code in the dynamic permission configuration:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(customUrlDecisionManager);
                        object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                        return object;
                    }
                })
                .and()
                ...
    }
}

How to understand with object postprocessor here?

Today, brother song will come to solve the mystery with you.

1. Function of objectpostprocessor

Let's first look at the function of ObjectPostProcessor. Let's first look at the definition of this interface:

package org.springframework.security.config.annotation;
public interface ObjectPostProcessor<T> {
    <O extends T> O postProcess(O object);
}

There is only one postProcess method in the interface.

Let's look at the inheritance relationship of ObjectPostProcessor:

There are two important implementation classes, among which AutowireBeanFactoryObjectPostProcessor is worth mentioning. Let's look at the definition of AutowireBeanFactoryObjectPostProcessor:

final class AutowireBeanFactoryObjectPostProcessor
        implements ObjectPostProcessor<Object>, DisposableBean, SmartInitializingSingleton {
    AutowireBeanFactoryObjectPostProcessor(
            AutowireCapableBeanFactory autowireBeanFactory) {
        this.autowireBeanFactory = autowireBeanFactory;
    }
    @SuppressWarnings("unchecked")
    public <T> T postProcess(T object) {
        if (object == null) {
            return null;
        }
        T result = null;
        try {
            result = (T) this.autowireBeanFactory.initializeBean(object,
                    object.toString());
        }
        catch (RuntimeException e) {
            Class<?> type = object.getClass();
            throw new RuntimeException(
                    "Could not postProcess " + object + " of type " + type, e);
        }
        this.autowireBeanFactory.autowireBean(object);
        if (result instanceof DisposableBean) {
            this.disposableBeans.add((DisposableBean) result);
        }
        if (result instanceof SmartInitializingSingleton) {
            this.smartSingletons.add((SmartInitializingSingleton) result);
        }
        return result;
    }
}

The source code of AutowireBeanFactoryObjectPostProcessor is well understood:

  1. First, when building the AutowireBeanFactoryObjectPostProcessor object, an AutowireCapableBeanFactory object is passed in. After reading the Spring source code, you will know that AutowireCapableBeanFactory can help us manually register an instance into the Spring container.
  2. In the postProcess method, it's the specific registration logic. It's very simple. I won't go over it again.

It can be seen that the main function of ObjectPostProcessor is to manually register the instance into the Spring container (and let the instance go through the Bean life cycle).

Normally, beans in our project are injected into the Spring container through automatic scanning. However, in the Spring Security framework, a large number of codes are not injected into the Spring container through automatic scanning, but are directly new. This is intended to simplify project configuration.

What should I do if I want to manage the code coming out of new directly by Spring container? So the ObjectPostProcessor is out.

2. Frame example

Next, I'll give you some examples of the object new in the framework. Let's take a look at the function of ObjectPostProcessor:

HttpSecurity initialization code (websecurityconfigureradapter ා gethttp):

protected final HttpSecurity getHttp() throws Exception {
    if (http != null) {
        return http;
    }
    ...
    ...
    http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
            sharedObjects);
    ...
    ...
}

WebSecurity initialization code (WebSecurity configuration? Setfilterchainproxysecurityconfigurer):

public void setFilterChainProxySecurityConfigurer(
        ObjectPostProcessor<Object> objectPostProcessor,
        @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
        throws Exception {
    webSecurity = objectPostProcessor
            .postProcess(new WebSecurity(objectPostProcessor));
    ...
    ...
}

Spring Security framework source code, manual assembly can be seen everywhere. In Spring Security, all filters in the filter chain are configured through the corresponding xxxConfigure, and all xxxConfigure are inherited from the SecurityConfigurerAdapter, as follows:

In the configuration methods of xxxConfigure, without exception, the managers they configure will go to the Spring container for a walk, such as abstractauthenticationfilterconfigurer

public void configure(B http) throws Exception {
    ...
    ...
    F filter = postProcess(authFilter);
    http.addFilter(filter);
}

Other methods of xxxconfiguer ා configure have similar implementations, so you can check them by yourself, so I won't go into details.

3. Why

Isn't it good to register beans directly to the Spring container through automatic scanning? Why are you doing this?

In Spring Security, because the framework itself adopts Java configuration in large quantities, and does not expose all the properties of the object, the intention of this is to simplify the configuration. However, one of the problems is that we need to register the Bean into the Spring container manually. ObjectPostProcessor is to solve this problem.

Once the Bean is registered in the Spring container, we have a way to enhance the function of a Bean, or we need to modify the properties of a Bean.

For example, the dynamic permission configuration code mentioned at the beginning:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(customUrlDecisionManager);
                        object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                        return object;
                    }
                })
                .and()
                ...
    }
}

Permission management itself is controlled by the FilterSecurityInterceptor. The default FilterSecurityInterceptor of the system has been created, and I can't modify its properties, so what should I do? We can use the withObjectPostProcessor method to modify the relevant properties in the FilterSecurityInterceptor.

One of the reasons why the above configuration takes effect is that after the FilterSecurityInterceptor is created successfully, it will go through the postProcess method again. Here, the property can be modified by overriding the postProcess method. We can see the method of configuring FilterSecurityInterceptor (abstractintercepturlconfigurer ᦇ configure):

abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
        extends AbstractHttpConfigurer<C, H> {
    @Override
    public void configure(H http) throws Exception {
        FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
        if (metadataSource == null) {
            return;
        }
        FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(
                http, metadataSource, http.getSharedObject(AuthenticationManager.class));
        if (filterSecurityInterceptorOncePerRequest != null) {
            securityInterceptor
                    .setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
        }
        securityInterceptor = postProcess(securityInterceptor);
        http.addFilter(securityInterceptor);
        http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
    }
}

You can see that after the securityInterceptor object is created successfully, it will still walk in the postProcess method.

Now that I understand the above code, let me give you another example. You should understand:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                ...
                .and()
                .formLogin()
                .withObjectPostProcessor(new ObjectPostProcessor<UsernamePasswordAuthenticationFilter>() {
                    @Override
                    public <O extends UsernamePasswordAuthenticationFilter> O postProcess(O object) {
                        object.setUsernameParameter("name");
                        return object;
                    }
                })
                ...
    }
}

Here, I will bring out the configured UsernamePasswordAuthenticationFilter filter and modify the user name key (normally, it is not so troublesome to modify the user name key, here is mainly to show you the effect of ObjectPostProcessor). After the modification, when the user logs in later, the user name is not username but name.

4. Summary

Well, as long as you master the above usage, in Spring Security, if you want to modify the properties of an object, but don't know where to start, you can try with objectpostprocessor!

Guys, if you think it's rewarding, please remember to watch and encourage brother song

Topics: Java Spring Session Database JSON