Spring Security系列之三 自定义短信登录认证

x33g5p2x  于2021-07-11 转载在 Java  
字(22.8k)|赞(0)|评价(0)|浏览(441)

自定义登录filter

上篇文章我们说到,对于用户的登录,security通过定义一个filter拦截login路径来实现的,所以我们要实现自定义登录,需要自己定义一个filter,继承AbstractAuthenticationProcessingFilter,从request中提取到手机号和验证码,然后提交给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() {
        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));
    }
}

其中SmsAuthenticationToken参照UsernamePasswordAuthenticationToken来实现:

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;
        //初始化完成,但是还未认证
        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() {
        return credentials;
    }

    @Override
    public Object getPrincipal() {
        return principal;
    }
}

自定义provider实现身份认证

我们知道AuthenticationManager最终会委托给Provider来实现身份验证,所以我们要判断验证码是否正确,需要自定义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");
        }
        //验证码是否正确
        if (!StringUtils.equals(CacheUtil.getValue(phone),verifyCode)){
            throw new AuthenticationCredentialsNotFoundException("验证码错误");
        }
        return new SmsAuthenticationToken(userDetails.getAuthorities(),userDetails,verifyCode);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.isAssignableFrom(SmsAuthenticationToken.class);
    }
}

上面的CacheUtil是封装的guava cache的实现,模拟发送验证码存储到内存中,在这个地方取出来做对比,如果对比失败就抛异常,对比成功就返回一个新的token,这个token中是包含了用户具有的权限的。

@Slf4j
public class CacheUtil {
    private static final LoadingCache<String, String> CACHE = CacheBuilder.newBuilder()
            //基于容量回收:总数量100个
            .maximumSize(100)
            //定时回收:没有写访问1分钟后失效清理
            .expireAfterWrite(1, TimeUnit.MINUTES)
            //当在缓存中未找到所需的缓存项时,会执行CacheLoader的load方法加载缓存
            .build(new CacheLoader<String, String>() {
                @Override
                public String load(String key) throws Exception {
                    log.debug("没有找到缓存: {}",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 "";
    }
}

身份认证结果回调

filter将手机号和验证码交给provider做验证,经过provider的校验,结果无非就两种,一种验证成功,一种验证失败,对于这两种不同的结果,我们需要实现两个handler,在获取到结果之后做回调。因为我们这儿只是简单的做url跳转,所以只需要继承SimpleUrlAuthenticationSuccessHandler

对于success的:

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

对于failure的:

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

上面整个登录流程的组件就完成了,接下来需要将它们整合起来。

整合登录组件

具体怎么整合,我们可以参考表单登录中,UsernamePasswordAuthenticationFilter是怎么整合进去的,回到配置类,还记得我们是怎么配置Security的吗:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login")  //登录页面
                .successForwardUrl("/index")  //登录成功后的页面
                .failureForwardUrl("/failure")  //登录失败后的页面
                .and()
                // 设置URL的授权
                .authorizeRequests()
                // 这里需要将登录页面放行
                .antMatchers("/login")
                .permitAll()
                //除了上面,其他所有请求必须被认证
                .anyRequest()
                .authenticated()
                .and()
                // 关闭csrf
                .csrf().disable();
    }
}

分析表单登录实现

看第一句,调用了http.formLogin(),在HttpSecurity的formLogin方法定义如下:

public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
return getOrApply(new FormLoginConfigurer<>());
}
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer)
throws Exception {
      //注意这个configure为SecurityConfigurerAdapter
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
return apply(configurer);
}

apply方法为AbstractConfiguredSecurityBuilder中的方法,我们目前先不关注它的实现,后面会仔细展开讲。现在只需要知道通过这个方法就能将configurer加入到security配置中。

这个地方添加了一个FormLoginConfigurer类,对于这个类官方给的解释为:

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.

翻译过来就是:

添加基于表单的身份验证。所有属性都有合理的默认值,从而使所有参数都是可选的。如果未指定loginPage,则框架将生成一个默认的登录页面。

看一下它的构造方法:

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

发现UsernamePasswordAuthenticationFilter被传递给了父类,我们去它的父类AbstractAuthenticationFilterConfigurer看一下:

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();
      //这个filter就是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);
}
      //通过getSharedObject获取共享对象。这里获取到AuthenticationManager
this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
      //设置成功和失败的回调
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);
      //添加filter
http.addFilter(filter);
}
}

可以看到这个地方主要做了三件事:

  1. 将AuthenticationManager设置到filter中
  2. 添加成功/失败的回调
  3. 将过滤器添加到过滤器链中

仿照表单登录,实现配置类

仿照上面的三个步骤,我们可以自己实现一个配置类,查看AbstractAuthenticationFilterConfigurer的类继承关系:

它最上面的顶级父类为SecurityConfigurerAdapter,我们就继承它来实现我们基本的配置就行了(也可以继承AbstractHttpConfigurer,没有歧视的意思),并且实现上面的三步:

@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 = new SmsAuthenticationFilter();
        smsAuthenticationFilter.setAuthenticationManager(builder.getSharedObject(AuthenticationManager.class));
        smsAuthenticationFilter.setAuthenticationSuccessHandler(smsAuthSuccessHandler);
        smsAuthenticationFilter.setAuthenticationFailureHandler(smsAuthFailureHandler);

        builder.authenticationProvider(smsAuthenticationProvider);
        builder.addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

和上面有一点不同,我们自定义的filter需要指定一下顺序,通过addFilterAfter方法将我们的filter添加到过滤器链中,并且将自定义的provider也一并配置了进来。

添加配置到security中

这样我们的所有组件就已经组合到一起了,修改一下配置类:

@Autowired
private SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
        .loginPage("/login")
        .and()
        .apply(smsAuthenticationSecurityConfig)
        .and()
        // 设置URL的授权
        .authorizeRequests()
        // 这里需要将登录页面放行
        .antMatchers("/login","/verifyCode","/smsLogin","/failure")
        .permitAll()
        // anyRequest() 所有请求   authenticated() 必须被认证
        .anyRequest()
        .authenticated()
        .and()
        // 关闭csrf
        .csrf().disable();
}

再修改一下登录页面的登录接口和字段名:

<!DOCTYPE html>
<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="提交"/>
    </form>
</body>
</html>

这样通过短信验证码登录的功能就已经实现了。

建议大家可以自己重新实现一个自定义邮箱验证码登录,加深映像。

源码分析

configurer配置类工作原理

上面只是简单的使用,接下来我们分析configure是如何工作的。

大家注意自己要打开idea跟着过一遍源码

其实通过上面的配置我们可以发现,在security中的过滤器其实都是通过各种xxxConfigure来进行配置的,我们可以简单的理解为filter就是和配置类绑定在一起的。明白了这个概念,我们继续往下分析。

看上面AbstractAuthenticationFilterConfigurer的类继承关系图,从最上面开始分析,SecurityBuilderSecurityConfigurer都是接口:

public interface SecurityBuilder<O> {
/**
 * 构建一个对象并返回
 */
O build() throws Exception;
}
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/**
 * 初始化
 */
void init(B builder) throws Exception;
void configure(B builder) throws Exception;

}

SecurityConfigurerAdapter分析

上面两个接口的具体实现交给了SecurityConfigurerAdapter,在spring security中很多配置类都是继承自SecurityConfigurerAdapter来实现的。看一下实现类SecurityConfigurerAdapter的源码:

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 {
}

/**
 * 返回SecurityBuilder,这样就可以进行链式调用了
 */
public B and() {
return getBuilder();
}

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

/**
 * 执行对象的后置处理。默认值为委派给ObjectPostProcessor完成
 * @return 可使用的已修改对象
 */
@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;
}

   /**
    * ObjectPostProcessor的一个实现
    */
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object postProcess(Object object) {
        //执行后置处理器的postProcess方法
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;
}
      //在list中添加了一个后置处理器
private boolean addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
boolean result = this.postProcessors.add(objectPostProcessor);
this.postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
return result;
}

}
}

嗯。。。这两个方法都是空实现,应该是交给后面的子类去自己重写方法。多出来的内容就只是初始化了CompositeObjectPostProcessor,并基于它封装了两个方法。

CompositeObjectPostProcessorObjectPostProcessor的一个实现,ObjectPostProcessor实际上是一个后置处理器。

其次addObjectPostProcessor方法实际上就是在list中添加了一个后置处理器并排序。然后在postProcess方法中对这个list遍历,判断ObjectPostProcessor泛型类型和传过来的参数类型是否为父子关系,再次调用postProcess方法。

这个地方可能有点疑惑,为什么要再调用一次postProcess,这不就成递归了吗,我们注意一下CompositeObjectPostProcessor类是private的,也就是只能在SecurityConfigurerAdapter内部使用,这里再次调用postProcess方法应该是其他的ObjectPostProcessor的实现。

可以看一下ObjectPostProcessor总共有两个实现,另外还有一个是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() {
for (SmartInitializingSingleton singleton : this.smartSingletons) {
singleton.afterSingletonsInstantiated();
}
}

@Override
public void destroy() {
for (DisposableBean disposable : this.disposableBeans) {
try {
disposable.destroy();
}
catch (Exception ex) {
this.logger.error(ex);
}
}
}
}

这里面主要是通过autowireBeanFactory将对象注入到容器当中,在security中,很多对象都是new出来的,这些new出来的对象和容器没有任何关联,也不方便管理,所以通过AutowireBeanFactoryObjectPostProcessor来完成对象的注入。

也就是说,在SecurityConfigurerAdapter中定义的这两个方法,其实就是将对象放进spring容器当中,方便管理。

AbstractConfiguredSecurityBuilder分析

SecurityConfigurerAdapter的内容就这么多了,继续往下看AbstractHttpConfigurer

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

@SuppressWarnings("unchecked")
public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (T) this;
}
}

代码很少,第二个方法就是调用SecurityConfigurerAdapter的方法,这里主要看第一个disable方法,我们在配置类中就已经使用过了, 在禁用csrf的时候调用了 csrf().disable(),就是通过这个方法,将csrf的配置移除了。

继续看disable方法是调用了AbstractConfiguredSecurityBuilder中的removeConfigurer方法,实际上就是移除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);
}

既然有移除的方法,那肯定就有添加的方法:

private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
private final Map<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);
        }
    }
}

我们自定义短信登录的时候,在配置类中添加自定义配置: .apply(smsAuthenticationSecurityConfig),这个apply方法实际上就是调用上面的方法,将配置添加了进去。

既然配置都添加到这个容器当中了,那什么时候取出来用呢:

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;
}
//执行所有configurer的初始化方法
private void init() 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);
    }
}
//获取到所有的configure,遍历执行configure方法
private void configure() throws Exception {
    //从LinkedHashMap中获取到configurer
    Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
    for (SecurityConfigurer<O, B> configurer : configurers) {
     configurer.configure((B) this);
    }
}

在init和configure方法中,调用了配置类的configure方法,到这里其实整个流程就已经通了。

我们一般自定义登录,都会实现这个configure方法,在这个方法里初始化一个filter,然后加入到过滤器链中。

而这个类的init和configure方法,实际上是在调用SecurityBuilder 的build方法被调用的,具体的代码链路就不说了,大家感兴趣的可以自己去看一下。

最后贴一下AbstractConfiguredSecurityBuilder的所有代码(已精简):

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 final Map<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
 */
public Map<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);
}
}
}


/**
 * 通过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);
}

/**
 * 通过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);
}

   //执行所有configurer的初始化方法
   private void init() 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);
}
}
   //获取到所有的configure,遍历执行configure方法
   private void configure() throws Exception {
      //从LinkedHashMap中获取到configurer
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
   //执行钩子函数和configure方法
   protected final O doBuild() 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;
return result;
}
}

}

相关文章