Spring Security series 3: Custom SMS Login Authentication

chapter

Spring Security is a series of simple introduction and practical

The second part of the Spring Security series analyzes the authentication process

Spring Security series 3: Custom SMS Login Authentication

The fourth in the Spring Security series uses JWT for authentication

Spring Security series # 5: User authorization for backend decoupage projects

Spring Security Series 6 authorization Process Analysis

Custom login filter

Article we said, for the user login, security by defining a filter to intercept the login path, so we’re going to implement custom login, the need to define a filter, inherit AbstractAuthenticationProcessingFilter, Extract the mobile phone number and verification code from the request and submit it to AuthenticationManager:

public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_PHONE_KEY = "phone";
    public static final String SPRING_SECURITY_FORM_VERIFY_CODE_KEY = "verifyCode";
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/smsLogin"."POST");
    protected SmsAuthenticationFilter(a) {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        String phone = request.getParameter(SPRING_SECURITY_FORM_PHONE_KEY);
        String verifyCode = request.getParameter(SPRING_SECURITY_FORM_VERIFY_CODE_KEY);
        if (StringUtils.isBlank(phone)){
            phone = "";
        }
        if (StringUtils.isBlank(verifyCode)){
            verifyCode = "";
        }
        SmsAuthenticationToken authenticationToken = new SmsAuthenticationToken(phone, verifyCode);
        setDetails(request,authenticationToken);
        return getAuthenticationManager().authenticate(authenticationToken);
    }

    protected void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); }}Copy the code

One SmsAuthenticationToken UsernamePasswordAuthenticationToken to implement:

public class SmsAuthenticationToken extends AbstractAuthenticationToken {
    private final Object principal;
    private Object credentials;

    public SmsAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        // Initialization is complete, but not yet authenticated
        setAuthenticated(false);
    }

    public SmsAuthenticationToken(Collection<? extends GrantedAuthority> authorities, Object principal, Object credentials) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        setAuthenticated(true);
    }

    @Override
    public Object getCredentials(a) {
        return credentials;
    }

    @Override
    public Object getPrincipal(a) {
        returnprincipal; }}Copy the code

User-defined Providers implement identity authentication

We know that the AuthenticationManager will eventually delegate to the Provider for authentication, so to determine whether the verification code is correct, we need to customize the Provider:

@Slf4j
@Component
public class SmsAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) {
        Assert.isInstanceOf(SmsAuthenticationToken.class, authentication,
                () ->  "SmsAuthenticationProvider.onlySupports Only SmsAuthenticationToken is supported");
        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;
        String phone = (String) authenticationToken.getPrincipal();
        String verifyCode = (String) authenticationToken.getCredentials();
        UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
        if (userDetails == null) {throw new InternalAuthenticationServiceException("cannot get user info");
        }
        // Verify the verification code is correct
        if(! StringUtils.equals(CacheUtil.getValue(phone),verifyCode)){throw new AuthenticationCredentialsNotFoundException("Verification code error");
        }
        return new SmsAuthenticationToken(userDetails.getAuthorities(),userDetails,verifyCode);
    }

    @Override
    public boolean supports(Class
        authentication) {
        returnauthentication.isAssignableFrom(SmsAuthenticationToken.class); }}Copy the code

The CacheUtil above is an implementation of the encapsulated Guava cache, which emulated sending a captcha into memory, where it is fetched for comparison. On failure an exception is thrown, and on success a new token is returned containing the user’s permissions.

@Slf4j
public class CacheUtil {
    private static final LoadingCache<String, String> CACHE = CacheBuilder.newBuilder()
            // Based on capacity: total number of 100
            .maximumSize(100)
            // Scheduled reclamation: If no write access is performed for 1 minute, it will be cleared
            .expireAfterWrite(1, TimeUnit.MINUTES)
            // When the desired entry is not found in the cache, the cache is loaded with the CacheLoader method
            .build(new CacheLoader<String, String>() {
                @Override
                public String load(String key) throws Exception {
                    log.debug("Cache not found: {}",key);
                    return ""; }});public static void putValue(String key, String value){
        CACHE.put(key,value);
    }

    public static String getValue(String key){
        try {
            return CACHE.get(key);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return ""; }}Copy the code

Authentication result callback

The filter sends the mobile phone number and verification code to the provider for verification. After verification by the provider, there are only two results: one is successful and the other is failed. For the two different results, we need to implement two handlers to perform callback after obtaining the results. Because we do here is simply a url to jump, so only need to inherit SimpleUrlAuthenticationSuccessHandler:

For SUCCESS:

@Component
public class SmsAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    public SmsAuthSuccessHandler(a) {
        super("/index"); }}Copy the code

For failure:

@Component
public class SmsAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    public SmsAuthFailureHandler(a) {
        super("/failure"); }}Copy the code

The components of the entire login process above are now complete, and you need to put them together.

Integrating login components

What specific integration, we can refer to login form, is how to integrate the UsernamePasswordAuthenticationFilter, back to the configuration class, remember how we configure Security:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login")  // Login page
                .successForwardUrl("/index")  // Successful login page
                .failureForwardUrl("/failure")  // Failed login page
                .and()
                // Set URL authorization
                .authorizeRequests()
                // The login page must be allowed
                .antMatchers("/login")
                .permitAll()
                // All requests other than the above must be authenticated
                .anyRequest()
                .authenticated()
                .and()
                / / close CSRF.csrf().disable(); }}Copy the code

Analyze the form login implementation

Call http.formLogin(). The formLogin method in HttpSecurity is defined as follows:

public FormLoginConfigurer<HttpSecurity> formLogin(a) throws Exception {
		return getOrApply(new FormLoginConfigurer<>());
}
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer)
			throws Exception {
      / / note that the configure SecurityConfigurerAdapter
		C existingConfig = (C) getConfigurer(configurer.getClass());
		if(existingConfig ! =null) {
			return existingConfig;
		}
		return apply(configurer);
}
Copy the code

The apply method in AbstractConfiguredSecurityBuilder method, we currently do not focus on its first implementation, later start speak carefully. Now all you need to know is how to add Configurer to your security configuration.

This adds a FormLoginConfigurer class, which is officially described as:

Adds form based authentication. All attributes have reasonable defaults making all parameters are optional. If no {@link #loginPage(String)} is specified, a default login page will be generated by the framework.

Translation:

Add form-based authentication. All properties have reasonable defaults, making all parameters optional. If loginPage is not specified, the framework will generate a default loginPage.

Take a look at its construction:

public FormLoginConfigurer(a) {
		super(new UsernamePasswordAuthenticationFilter(), null);
		usernameParameter("username");
		passwordParameter("password");
}
Copy the code

Found UsernamePasswordAuthenticationFilter is passed to the parent class, we went to its superclass AbstractAuthenticationFilterConfigurer look at:

public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B.T.F>, F extends AbstractAuthenticationProcessingFilter>
		extends AbstractHttpConfigurer<T.B> {
		
	protected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) {
		this(a);This filter is UsernamePasswordAuthenticationFilter / /
		this.authFilter = authenticationFilter;
		if(defaultLoginProcessingUrl ! =null) { loginProcessingUrl(defaultLoginProcessingUrl); }}@Override
	public void configure(B http) throws Exception {
		PortMapper portMapper = http.getSharedObject(PortMapper.class);
		if(portMapper ! =null) {
			this.authenticationEntryPoint.setPortMapper(portMapper);
		}
		RequestCache requestCache = http.getSharedObject(RequestCache.class);
		if(requestCache ! =null) {
			this.defaultSuccessHandler.setRequestCache(requestCache);
		}
      // Get the shared object with getSharedObject. Here you get the AuthenticationManager
		this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
      // Set successful and failed callbacks
		this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
		this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
		if (this.authenticationDetailsSource ! =null) {
			this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
		}
		SessionAuthenticationStrategy sessionAuthenticationStrategy = http
				.getSharedObject(SessionAuthenticationStrategy.class);
		if(sessionAuthenticationStrategy ! =null) {
			this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
		}
		RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
		if(rememberMeServices ! =null) {
			this.authFilter.setRememberMeServices(rememberMeServices);
		}
		F filter = postProcess(this.authFilter);
      / / add the filterhttp.addFilter(filter); }}Copy the code

You can see that there are three main things that this place does:

  1. Set AuthenticationManager to filter
  2. Add successful/failed callbacks
  3. Add the filter to the filter chain

Implement the configuration class, modeled after the form login

To simulate the three steps above, we can achieve a configuration class, our view AbstractAuthenticationFilterConfigurer class inheritance relationships:

It top the top of the parent class for SecurityConfigurerAdapter, we inherit it to achieve our basic configuration of the line (also can inherit AbstractHttpConfigurer, without discrimination “), and to realize the above three steps:

@Component
public class SmsAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain.HttpSecurity> {
    @Autowired
    private SmsAuthSuccessHandler smsAuthSuccessHandler;
    @Autowired
    private SmsAuthFailureHandler smsAuthFailureHandler;
    @Autowired
    private SmsAuthenticationProvider smsAuthenticationProvider;
    @Override
    public void configure(HttpSecurity builder) throws Exception {
        SmsAuthenticationFilter smsAuthenticationFilter = newSmsAuthenticationFilter(); smsAuthenticationFilter.setAuthenticationManager(builder.getSharedObject(AuthenticationManager.class)); smsAuthenticationFilter.setAuthenticationSuccessHandler(smsAuthSuccessHandler); smsAuthenticationFilter.setAuthenticationFailureHandler(smsAuthFailureHandler); builder.authenticationProvider(smsAuthenticationProvider); builder.addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); }}Copy the code

Different from the above, we need to specify the order of our custom filters, add our filters to the filter chain through the addFilterAfter method, and configure the custom provider as well.

Add configuration to Security

Now that all of our components have been assembled, modify the configuration class:

@Autowired
private SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
        .loginPage("/login")
        .and()
        .apply(smsAuthenticationSecurityConfig)
        .and()
        // Set URL authorization
        .authorizeRequests()
        // The login page must be allowed
        .antMatchers("/login"."/verifyCode"."/smsLogin"."/failure")
        .permitAll()
        // anyRequest() All requests authenticated() must be authenticated
        .anyRequest()
        .authenticated()
        .and()
        / / close CSRF
        .csrf().disable();
}
Copy the code

Modify the login interface and field name of the login page:

<! DOCTYPEhtml>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
    <form action="/smsLogin" method="post">
        <input type="text" name="phone"/>
        <input type="password" name="verifyCode"/>
        <input type="submit" value="Submit"/>
    </form>
</body>
</html>
Copy the code

In this way, the function of logging in by SMS verification code has been realized.

We suggest that you can re-implement a custom mailbox verification code login, deepen the image.

Source code analysis

The Configurer configuration class works

With the above simple usage, let’s analyze how configure works.

We pay attention to their own idea to open with a source code

In fact, through the above configuration we can find that the filter in security is actually configured through a variety of xxxConfigure, we can simply understand that filter is bound to the configuration class. Now that we understand this concept, let’s move on.

See above AbstractAuthenticationFilterConfigurer class inheritance diagram, from the above analysis, then SecurityBuilder and SecurityConfigurer interface:

public interface SecurityBuilder<O> {
	/** * builds an object and returns */
	O build(a) throws Exception;
}
public interface SecurityConfigurer<O.B extends SecurityBuilder<O>> {
	/** * initializes */
	void init(B builder) throws Exception;
	void configure(B builder) throws Exception;

}
Copy the code

SecurityConfigurerAdapter analysis

The above two the concrete implementation of the interface to the SecurityConfigurerAdapter, many in the spring security configuration class are inherited from SecurityConfigurerAdapter. Look at the implementation class SecurityConfigurerAdapter source code:

public abstract class SecurityConfigurerAdapter<O.B extends SecurityBuilder<O>> implements SecurityConfigurer<O.B> {

	private B securityBuilder;

	private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();

	@Override
	public void init(B builder) throws Exception {}@Override
	public void configure(B builder) throws Exception {}/** * returns the SecurityBuilder so that the chain call can be made to */
	public B and(a) {
		return getBuilder();
	}

	/** * Get SecurityBuilder */
	protected final B getBuilder(a) {
		Assert.state(this.securityBuilder ! =null."securityBuilder cannot be null");
		return this.securityBuilder;
	}

	/** * performs post-processing of the object. The default value is delegate to ObjectPostProcessor completion *@returnAvailable modified objects */
	@SuppressWarnings("unchecked")
	protected <T> T postProcess(T object) {
		return (T) this.objectPostProcessor.postProcess(object);
	}

	public void addObjectPostProcessor(ObjectPostProcessor
        objectPostProcessor) {
		this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
	}

	public void setBuilder(B builder) {
		this.securityBuilder = builder;
	}

   /** * an implementation of ObjectPostProcessor */
	private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
		privateList<ObjectPostProcessor<? >> postProcessors =new ArrayList<>();
		@Override
		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Object postProcess(Object object) {
        // Executes the postProcess method of the post-processor
			for (ObjectPostProcessor opp : this.postProcessors) { Class<? > oppClass = opp.getClass(); Class<? > oppType = GenericTypeResolver.resolveTypeArgument(oppClass, ObjectPostProcessor.class);if (oppType == null|| oppType.isAssignableFrom(object.getClass())) { object = opp.postProcess(object); }}return object;
		}
      // Add a post-processor to the list
		private boolean addObjectPostProcessor(ObjectPostProcessor
        objectPostProcessor) {
			boolean result = this.postProcessors.add(objectPostProcessor);
			this.postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
			returnresult; }}}Copy the code

B: well… Both methods are empty implementations and should be left to subclasses to override the methods themselves. Extra content just to initialize the CompositeObjectPostProcessor, and based on it encapsulates the two methods.

CompositeObjectPostProcessor ObjectPostProcessor is an implementation, rear ObjectPostProcessor is actually a processor.

The second addObjectPostProcessor method actually adds a post-processor to the list and sorts it. The list is then iterated through in the postProcess method to determine whether the ObjectPostProcessor generic type and the passed parameter type are parent-child, and the postProcess method is called again.

This place may be a bit confused, why do you want to call again postProcess, it’s not just into recursion, we pay attention to the CompositeObjectPostProcessor class is private, Can only be used inside the SecurityConfigurerAdapter, here again call postProcess method should be other ObjectPostProcessor implementation.

There were a total of two implementations can have a look at ObjectPostProcessor, another is AutowireBeanFactoryObjectPostProcessor:

final class AutowireBeanFactoryObjectPostProcessor
		implements ObjectPostProcessor<Object>, DisposableBean.SmartInitializingSingleton {

	private final Log logger = LogFactory.getLog(getClass());

	private final AutowireCapableBeanFactory autowireBeanFactory;

	private final List<DisposableBean> disposableBeans = new ArrayList<>();

	private final List<SmartInitializingSingleton> smartSingletons = new ArrayList<>();

	AutowireBeanFactoryObjectPostProcessor(AutowireCapableBeanFactory autowireBeanFactory) {
		Assert.notNull(autowireBeanFactory, "autowireBeanFactory cannot be null");
		this.autowireBeanFactory = autowireBeanFactory;
	}

	@Override
	@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 ex) { Class<? > type = object.getClass();throw new RuntimeException("Could not postProcess " + object + " of type " + type, ex);
		}
		this.autowireBeanFactory.autowireBean(object);
		if (result instanceof DisposableBean) {
			this.disposableBeans.add((DisposableBean) result);
		}
		if (result instanceof SmartInitializingSingleton) {
			this.smartSingletons.add((SmartInitializingSingleton) result);
		}
		return result;
	}

	@Override
	public void afterSingletonsInstantiated(a) {
		for (SmartInitializingSingleton singleton : this.smartSingletons) { singleton.afterSingletonsInstantiated(); }}@Override
	public void destroy(a) {
		for (DisposableBean disposable : this.disposableBeans) {
			try {
				disposable.destroy();
			}
			catch (Exception ex) {
				this.logger.error(ex); }}}}Copy the code

In security, many objects are new. These new objects have no association with the container and are not easy to manage. So done through AutowireBeanFactoryObjectPostProcessor injection of objects.

That is to say, the definition of these two methods in SecurityConfigurerAdapter, is actually put objects into the spring container, convenient management.

AbstractConfiguredSecurityBuilder analysis

SecurityConfigurerAdapter content so much, continue to look down AbstractHttpConfigurer:

public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T.B>, B extends HttpSecurityBuilder<B>>
		extends SecurityConfigurerAdapter<DefaultSecurityFilterChain.B> {
	@SuppressWarnings("unchecked")
	public B disable(a) {
		getBuilder().removeConfigurer(getClass());
		return getBuilder();
	}

	@SuppressWarnings("unchecked")
	public T withObjectPostProcessor(ObjectPostProcessor
        objectPostProcessor) {
		addObjectPostProcessor(objectPostProcessor);
		return (T) this; }}Copy the code

Very little code, the second method is called SecurityConfigurerAdapter method, here basically see first disable method, we have been used in the configuration class, called when disable CSRF the CSRF (). The disable (), it is through this method, Remove the CSRF configuration.

Continue to see the disable method is to call the removeConfigurer AbstractConfiguredSecurityBuilder method, in fact is one of the elements in the remove LinkedHashMap:

private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
		List<C> configs = (List<C>) this.configurers.remove(clazz);
		if (configs == null) {
			return new ArrayList<>();
		}
		return new ArrayList<>(configs);
	}
Copy the code

If there is a way to remove, there must be a way to add:

private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
private finalMap<Class<? >, Object> sharedObjects =new HashMap<>();
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
    Assert.notNull(configurer, "configurer cannot be null");
    Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
        .getClass();
    synchronized (this.configurers) {
        if (this.buildState.isConfigured()) {
            throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
        }
        List<SecurityConfigurer<O, B>> configs = null;
        if (this.allowConfigurersOfSameType) {
            configs = this.configurers.get(clazz); } configs = (configs ! =null)? configs :new ArrayList<>(1);
        configs.add(configurer);
        this.configurers.put(clazz, configs);
        if (this.buildState.isInitializing()) {
            this.configurersAddedInInitializing.add(configurer); }}}Copy the code

We custom message login, add custom configuration in the configuration class: apply (smsAuthenticationSecurityConfig), the apply method is actually call the above method, add the configuration to go in.

Now that configuration has been added to the container, when should it be taken out and used:

private Collection<SecurityConfigurer<O, B>> getConfigurers() {
		List<SecurityConfigurer<O, B>> result = new ArrayList<>();
		for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
			result.addAll(configs);
		}
		return result;
}
// Execute all configurer initialization methods
private void init(a) throws Exception {
    Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
    for (SecurityConfigurer<O, B> configurer : configurers) {
    	configurer.init((B) this);
    }
    for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
    	configurer.init((B) this); }}// Get all of configure, iterate through the configure method
private void configure(a) throws Exception {
    // Get configurer from LinkedHashMap
    Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
    for (SecurityConfigurer<O, B> configurer : configurers) {
    	configurer.configure((B) this); }}Copy the code

In the init and configure methods, we call the configure method of the configuration class, and at this point the whole process is complete.

For custom logins, we implement the configure method, which initializes a filter and adds it to the filter chain.

The class init and configure methods are called when the build method of SecurityBuilder is called.

Last posted once all AbstractConfiguredSecurityBuilder code (streamline) :

public abstract class AbstractConfiguredSecurityBuilder<O.B extends SecurityBuilder<O>>
		extends AbstractSecurityBuilder<O> {
	private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
	private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
	private finalMap<Class<? >, Object> sharedObjects =new HashMap<>();
	private final boolean allowConfigurersOfSameType;
	private ObjectPostProcessor<Object> objectPostProcessor;

	@SuppressWarnings("unchecked")
	public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
		configurer.addObjectPostProcessor(this.objectPostProcessor);
		configurer.setBuilder((B) this);
		add(configurer);
		return configurer;
	}

	public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
		add(configurer);
		return configurer;
	}

	@SuppressWarnings("unchecked")
	public <C> void setSharedObject(Class<C> sharedType, C object) {
		this.sharedObjects.put(sharedType, object);
	}

	@SuppressWarnings("unchecked")
	public <C> C getSharedObject(Class<C> sharedType) {
		return (C) this.sharedObjects.get(sharedType);
	}

	/**
	 * Gets the shared objects
	 * @return the shared Objects
	 */
	publicMap<Class<? >, Object> getSharedObjects() {return Collections.unmodifiableMap(this.sharedObjects);
	}
    
	@SuppressWarnings("unchecked")
	private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
		Assert.notNull(configurer, "configurer cannot be null");
		Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
				.getClass();
		synchronized (this.configurers) {
			if (this.buildState.isConfigured()) {
				throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
			}
			List<SecurityConfigurer<O, B>> configs = null;
			if (this.allowConfigurersOfSameType) {
				configs = this.configurers.get(clazz); } configs = (configs ! =null)? configs :new ArrayList<>(1);
			configs.add(configurer);
			this.configurers.put(clazz, configs);
			if (this.buildState.isInitializing()) {
				this.configurersAddedInInitializing.add(configurer); }}}/** * Remove the associated configuration class */ by class name
	@SuppressWarnings("unchecked")
	public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
		List<C> configs = (List<C>) this.configurers.remove(clazz);
		if (configs == null) {
			return new ArrayList<>();
		}
		return new ArrayList<>(configs);
	}

	/** * Remove the associated configuration class */ by class name
	@SuppressWarnings("unchecked")
	public <C extends SecurityConfigurer<O, B>> C removeConfigurer(Class<C> clazz) {
		List<SecurityConfigurer<O, B>> configs = this.configurers.remove(clazz);
		if (configs == null) {
			return null;
		}
		Assert.state(configs.size() == 1, () - >"Only one configurer expected for type " + clazz + ", but got " + configs);
		return (C) configs.get(0);
	}

	@SuppressWarnings("unchecked")
	public B objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
		Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
		this.objectPostProcessor = objectPostProcessor;
		return (B) this;
	}

	protected <P> P postProcess(P object) {
		return this.objectPostProcessor.postProcess(object);
	}

   // Execute all configurer initialization methods
   private void init(a) throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.init((B) this);
		}
		for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
			configurer.init((B) this); }}// Get all of configure, iterate through the configure method
   private void configure(a) throws Exception {
      // Get configurer from LinkedHashMap
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this); }}// Execute the hook function and configure method
   protected final O doBuild(a) throws Exception {
		synchronized (this.configurers) {
			this.buildState = BuildState.INITIALIZING;
			beforeInit();
			init();
			this.buildState = BuildState.CONFIGURING;
			beforeConfigure();
			configure();
			this.buildState = BuildState.BUILDING;
			O result = performBuild();
			this.buildState = BuildState.BUILT;
			returnresult; }}}Copy the code