乐趣区

关于java:深入理解-HttpSecurity源码篇

HttpSecurity 也是 Spring Security 中的重要一环。咱们平时所做的大部分 Spring Security 配置也都是基于 HttpSecurity 来配置的。因而咱们有必要从源码的角度来了解下 HttpSecurity 到底干了啥?

1. 抽丝剥茧

首先咱们来看下 HttpSecurity 的继承关系图:

能够看到,HttpSecurity 继承自 AbstractConfiguredSecurityBuilder,同时实现了 SecurityBuilder 和 HttpSecurityBuilder 两个接口。

咱们来看下 HttpSecurity 的定义:

public final class HttpSecurity extends
        AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
        implements SecurityBuilder<DefaultSecurityFilterChain>,
        HttpSecurityBuilder<HttpSecurity> {//...}

这里每一个类都带有泛型,看得人有点目迷五色。

我把这个泛型类拿进去和大家讲一下,小伙伴们就明确了。

泛型次要是两个,DefaultSecurityFilterChain 和 HttpSecurity,HttpSecurity 就不用说了,这是咱们明天的配角,那么 DefaultSecurityFilterChain 是干嘛的?

这咱们就得从 SecurityFilterChain 说起了。

1.1 SecurityFilterChain

先来看定义:

public interface SecurityFilterChain {boolean matches(HttpServletRequest request);
    List<Filter> getFilters();}

SecurityFilterChain 其实就是咱们平时所说的 Spring Security 中的过滤器链,它里边定义了两个办法,一个是 matches 办法用来匹配申请,另外一个 getFilters 办法返回一个 List 汇合,汇合中放着 Filter 对象,当一个申请到来时,用 matches 办法去比拟申请是否和以后链吻合,如果吻合,就返回 getFilters 办法中的过滤器,那么以后申请会一一通过 List 汇合中的过滤器。这一点,小伙伴们能够回顾后面【深刻了解 FilterChainProxy【源码篇】】一文。

SecurityFilterChain 接口只有一个实现类,那就是 DefaultSecurityFilterChain:

public final class DefaultSecurityFilterChain implements SecurityFilterChain {private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
    private final RequestMatcher requestMatcher;
    private final List<Filter> filters;

    public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {this(requestMatcher, Arrays.asList(filters));
    }

    public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {logger.info("Creating filter chain:" + requestMatcher + "," + filters);
        this.requestMatcher = requestMatcher;
        this.filters = new ArrayList<>(filters);
    }

    public RequestMatcher getRequestMatcher() {return requestMatcher;}

    public List<Filter> getFilters() {return filters;}

    public boolean matches(HttpServletRequest request) {return requestMatcher.matches(request);
    }

    @Override
    public String toString() {return "[" + requestMatcher + "," + filters + "]";
    }
}

DefaultSecurityFilterChain 只是对 SecurityFilterChain 中的办法进行了实现,并没有特地值得说的中央,松哥也就不啰嗦了。

那么从下面的介绍中,大家能够看到,DefaultSecurityFilterChain 其实就相当于是 Spring Security 中的过滤器链,一个 DefaultSecurityFilterChain 代表一个过滤器链,如果零碎中存在多个过滤器链,则会存在多个 DefaultSecurityFilterChain 对象。

接下来咱们把 HttpSecurity 的这几个父类捋一捋。

1.2 SecurityBuilder

public interface SecurityBuilder<O> {O build() throws Exception;
}

SecurityBuilder 就是用来构建过滤器链的,在 HttpSecurity 实现 SecurityBuilder 时,传入的泛型就是 DefaultSecurityFilterChain,所以 SecurityBuilder#build 办法的性能很明确,就是用来构建一个过滤器链进去。

1.3 HttpSecurityBuilder

HttpSecurityBuilder 看名字就是用来构建 HttpSecurity 的。不过它也只是一个接口,具体的实现在 HttpSecurity 中,接口定义如下:

public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
        SecurityBuilder<DefaultSecurityFilterChain> {
    <C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz);
    <C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz);
    <C> void setSharedObject(Class<C> sharedType, C object);
    <C> C getSharedObject(Class<C> sharedType);
    H authenticationProvider(AuthenticationProvider authenticationProvider);
    H userDetailsService(UserDetailsService userDetailsService) throws Exception;
    H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);
    H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);
    H addFilter(Filter filter);
}

这里的办法比较简单:

  1. getConfigurer 获取一个配置对象。Spring Security 过滤器链中的所有过滤器对象都是由 xxxConfigure 来进行配置的,这里就是获取这个 xxxConfigure 对象。
  2. removeConfigurer 移除一个配置对象。
  3. setSharedObject/getSharedObject 配置 / 获取由多个 SecurityConfigurer 共享的对象。
  4. authenticationProvider 办法示意配置验证器。
  5. userDetailsService 配置数据源接口。
  6. addFilterAfter 在某一个过滤器之前增加过滤器。
  7. addFilterBefore 在某一个过滤器之后增加过滤器。
  8. addFilter 增加一个过滤器,该过滤器必须是现有过滤器链中某一个过滤器或者其扩大。

这便是 HttpSecurityBuilder 中的性能,这些接口在 HttpSecurity 中都将失去实现。

1.4 AbstractSecurityBuilder

AbstractSecurityBuilder 类实现了 SecurityBuilder 接口,该类中次要做了一件事,就是确保整个构建只被构建一次。

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {private AtomicBoolean building = new AtomicBoolean();
    private O object;
    public final O build() throws Exception {if (this.building.compareAndSet(false, true)) {this.object = doBuild();
            return this.object;
        }
        throw new AlreadyBuiltException("This object has already been built");
    }
    public final O getObject() {if (!this.building.get()) {throw new IllegalStateException("This object has not been built");
        }
        return this.object;
    }
    protected abstract O doBuild() throws Exception;}

能够看到,这里从新定义了 build 办法,并设置 build 办法为 final 类型,无奈被重写,在 build 办法中,通过 AtomicBoolean 实现该办法只被调用一次。具体的构建逻辑则定义了新的形象办法 doBuild,未来在实现类中通过 doBuild 办法定义构建逻辑。

1.5 AbstractConfiguredSecurityBuilder

AbstractSecurityBuilder 办法的实现类就是 AbstractConfiguredSecurityBuilder。

AbstractConfiguredSecurityBuilder 中所做的事件就比拟多了,咱们别离来看。

首先 AbstractConfiguredSecurityBuilder 中定义了一个枚举类,将整个构建过程分为 5 种状态,也能够了解为构建过程生命周期的五个阶段,如下:

private enum BuildState {UNBUILT(0),
    INITIALIZING(1),
    CONFIGURING(2),
    BUILDING(3),
    BUILT(4);
    private final int order;
    BuildState(int order) {this.order = order;}
    public boolean isInitializing() {return INITIALIZING.order == order;}
    public boolean isConfigured() {return order >= CONFIGURING.order;}
}

五种状态别离是 UNBUILT、INITIALIZING、CONFIGURING、BUILDING 以及 BUILT。另外还提供了两个判断办法,isInitializing 判断是否正在初始化,isConfigured 示意是否曾经配置结束。

AbstractConfiguredSecurityBuilder 中的办法比拟多,松哥在这里列出来两个要害的办法和大家剖析:

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 (configurers) {if (buildState.isConfigured()) {
            throw new IllegalStateException("Cannot apply" + configurer
                    + "to already built object");
        }
        List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
                .get(clazz) : null;
        if (configs == null) {configs = new ArrayList<>(1);
        }
        configs.add(configurer);
        this.configurers.put(clazz, configs);
        if (buildState.isInitializing()) {this.configurersAddedInInitializing.add(configurer);
        }
    }
}
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;
}

第一个就是这个 add 办法,这相当于是在收集所有的配置类。将所有的 xxxConfigure 收集起来存储到 configurers 中,未来再对立初始化并配置,configurers 自身是一个 LinkedHashMap,key 是配置类的 class,value 是一个汇合,汇合里边放着 xxxConfigure 配置类。当须要对这些配置类进行集中配置的时候,会通过 getConfigurers 办法获取配置类,这个获取过程就是把 LinkedHashMap 中的 value 拿进去,放到一个汇合中返回。

另一个办法就是 doBuild 办法。

@Override
protected final O doBuild() throws Exception {synchronized (configurers) {
        buildState = BuildState.INITIALIZING;
        beforeInit();
        init();
        buildState = BuildState.CONFIGURING;
        beforeConfigure();
        configure();
        buildState = BuildState.BUILDING;
        O result = performBuild();
        buildState = BuildState.BUILT;
        return result;
    }
}
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 : configurersAddedInInitializing) {configurer.init((B) this);
    }
}
private void configure() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
    for (SecurityConfigurer<O, B> configurer : configurers) {configurer.configure((B) this);
    }
}

在 AbstractSecurityBuilder 类中,过滤器的构建被转移到 doBuild 办法下面了,不过在 AbstractSecurityBuilder 中只是定义了形象的 doBuild 办法,具体的实现在 AbstractConfiguredSecurityBuilder。

doBuild 办法就是一边更新状态,进行进行初始化。

beforeInit 是一个预留办法,没有任何实现。

init 办法就是找到所有的 xxxConfigure,挨个调用其 init 办法进行初始化。

beforeConfigure 是一个预留办法,没有任何实现。

configure 办法就是找到所有的 xxxConfigure,挨个调用其 configure 办法进行配置。

最初则是 performBuild 办法,是真正的过滤器链构建办法,然而在 AbstractConfiguredSecurityBuilder 中 performBuild 办法只是一个形象办法,具体的实现在 HttpSecurity 中。

这便是 HttpSecurity 所有父类、父接口的性能。

看完了父辈,接下来回到咱们明天文章的主题,HttpSecurity。

2. HttpSecurity

HttpSecurity 做的事件,就是进行各种各样的 xxxConfigurer 配置。

轻易举几例:

public CorsConfigurer<HttpSecurity> cors() throws Exception {return getOrApply(new CorsConfigurer<>());
}
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {ApplicationContext context = getContext();
    return getOrApply(new CsrfConfigurer<>(context));
}
public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {return getOrApply(new ExceptionHandlingConfigurer<>());
}

HttpSecurity 中有大量相似的办法,过滤器链中的过滤器就是这样一个一个配置的。我就不一一介绍了。

每个配置办法的结尾都会来一句 getOrApply,这个是干嘛的?

private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer) throws Exception {C existingConfig = (C) getConfigurer(configurer.getClass());
    if (existingConfig != null) {return existingConfig;}
    return apply(configurer);
}

getConfigurer 办法是在它的父类 AbstractConfiguredSecurityBuilder 中定义的,目标就是去查看以后这个 xxxConfigurer 是否曾经配置过了。

如果以后 xxxConfigurer 曾经配置过了,则间接返回,否则调用 apply 办法,这个 apply 办法最终会调用到 AbstractConfiguredSecurityBuilder#add 办法,将以后配置 configurer 收集起来。

HttpSecurity 中还有一个 addFilter 办法:

public HttpSecurity addFilter(Filter filter) {Class<? extends Filter> filterClass = filter.getClass();
    if (!comparator.isRegistered(filterClass)) {
        throw new IllegalArgumentException(
                "The Filter class"
                        + filterClass.getName()
                        + "does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
    }
    this.filters.add(filter);
    return this;
}

这个 addFilter 办法的作用,次要是在各个 xxxConfigurer 进行配置的时候,会调用到这个办法,(xxxConfigurer 就是用来配置过滤器的),把 Filter 都增加到 fitlers 变量中。

最终在 HttpSecurity 的 performBuild 办法中,构建进去一个过滤器链:

@Override
protected DefaultSecurityFilterChain performBuild() {filters.sort(comparator);
    return new DefaultSecurityFilterChain(requestMatcher, filters);
}

先给过滤器排序,而后结构 DefaultSecurityFilterChain 对象。

3. 小结

好啦,这就是 HttpSecurity 的一个大抵工作流程。把握住了这个工作流程,剩下的就只是一些简略的反复的 xxxConfigurer 配置了,松哥就不再啰嗦啦。

如果小伙伴们感觉有播种,记得点个在看激励下松哥哦~

退出移动版