相关文章推荐
苦恼的打火机  ·  CSS – Sass & SCSS - ...·  9 月前    · 

HttpSecurity

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

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

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

我们来看下 HttpSecurity 的定义:

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

这里每一个类都带有泛型,看得人有点眼花缭乱。

我把这个泛型类拿出来和大家讲一下,小伙伴们就明白了。

泛型主要是两个,DefaultSecurityFilterChain 和 HttpSecurity,HttpSecurity 就不用说了,这是我们今天的主角,那么 DefaultSecurityFilterChain 是干嘛的?

这我们就得从 SecurityFilterChain 说起了。

SecurityFilterChain

先来看定义:

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

SecurityFilterChain 其实就是我们平时所说的 Spring Security 中的过滤器链,它里边定义了两个方法,一个是 matches 方法用来匹配请求,另外一个 getFilters 方法返回一个 List 集合,集合中放着 Filter 对象,当一个请求到来时,用 matches 方法去比较请求是否和当前链吻合,如果吻合,就返回 getFilters 方法中的过滤器,那么当前请求会逐个经过 List 集合中的过滤器。

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 的这几个父类捋一捋。

SecurityBuilder

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

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

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);

这里的方法比较简单:

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

这便是 HttpSecurityBuilder 中的功能,这些接口在 HttpSecurity 中都将得到实现。

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 方法定义构建逻辑。

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。

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 对象。

FilterChainProxy

我们可以看到一个过滤器链Spring Security Filter 封装着多个过滤器 Filter,多个过滤器链封装在一个FilterChainProxy中

可以看到,Spring Security Filter 并不是直接嵌入到 Web Filter 中的,而是通过 FilterChainProxy 来统一管理 Spring Security Filter,FilterChainProxy 本身则通过 Spring 提供的 DelegatingFilterProxy 代理过滤器嵌入到 Web Filter 之中。

FilterChainProxy 中可以存在多个过滤器链,如下图:

可以看到,当请求到达 FilterChainProxy 之后,FilterChainProxy 会根据请求的路径,将请求转发到不同的 Spring Security Filters 上面去,不同的 Spring Security Filters 对应了不同的过滤器,也就是不同的请求将经过不同的过滤器。

这是 FilterChainProxy 的一个大致功能,今天我们就从源码上理解 FilterChainProxy 中这些功能到底是怎么实现的。

先把 FilterChainProxy 源码亮出来,这个源码比较上,我们一部分一部分来,先从它声明的全局属性上开始:

private final static String FILTER_APPLIED = FilterChainProxy.class.getName().concat(
		".APPLIED");
private List<SecurityFilterChain> filterChains;
private FilterChainValidator filterChainValidator = new NullFilterChainValidator();
private HttpFirewall firewall = new StrictHttpFirewall();

FILTER_APPLIED 变量是一个标记,用来标记过滤器是否已经执行过了。这个标记在 Spring Security 中很常见,松哥这里就不多说了。
filterChains 是过滤器链,注意,这个是过滤器链,而不是一个个的过滤器。
filterChainValidator 是 FilterChainProxy 配置完成后的校验方法,默认使用的 NullFilterChainValidator 实际上对应了一个空方法,也就是不做任何校验。
firewall 我们在前面的文章中也介绍过,这里就不再赘述。

接下来我们来看一个过滤器中最重要的 doFilter 方法:

@Override
public void doFilter(ServletRequest request, ServletResponse response,
		FilterChain chain) throws IOException, ServletException {
	boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
	if (clearContext) {
		try {
			request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
			doFilterInternal(request, response, chain);
		finally {
			SecurityContextHolder.clearContext();
			request.removeAttribute(FILTER_APPLIED);
	else {
		doFilterInternal(request, response, chain);

在 doFilter 方法中,正常来说,clearContext 参数每次都是 true,于是每次都先给 request 标记上 FILTER_APPLIED 属性,然后执行 doFilterInternal 方法去走过滤器,执行完毕后,最后在 finally 代码块中清除 SecurityContextHolder 中保存的用户信息,同时移除 request 中的标记。

按着这个顺序,我们来看 doFilterInternal 方法:

private void doFilterInternal(ServletRequest request, ServletResponse response,
		FilterChain chain) throws IOException, ServletException {
	FirewalledRequest fwRequest = firewall
			.getFirewalledRequest((HttpServletRequest) request);
	HttpServletResponse fwResponse = firewall
			.getFirewalledResponse((HttpServletResponse) response);
	List<Filter> filters = getFilters(fwRequest);
	if (filters == null || filters.size() == 0) {
		if (logger.isDebugEnabled()) {
			logger.debug(UrlUtils.buildRequestUrl(fwRequest)
					+ (filters == null ? " has no matching filters"
							: " has an empty filter list"));
		fwRequest.reset();
		chain.doFilter(fwRequest, fwResponse);
		return;
	VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
	vfc.doFilter(fwRequest, fwResponse);
private List<Filter> getFilters(HttpServletRequest request) {
	for (SecurityFilterChain chain : filterChains) {
		if (chain.matches(request)) {
			return chain.getFilters();
	return null;

1.首先将请求封装为一个 FirewalledRequest 对象,在这个封装的过程中,也会判断请求是否合法。
2.调用 getFilters 方法找到过滤器链。该方法就是根据当前的请求,从 filterChains 中找到对应的过滤器链,然后由该过滤器链去处理请求
3.如果找出来的 filters 为 null,或者集合中没有元素,那就是说明当前请求不需要经过过滤器。直接执行 chain.doFilter ,这个就又回到原生过滤器中去了。那么什么时候会发生这种情况呢?那就是针对项目中的静态资源,如果我们配置了资源放行,如 web.ignoring().antMatchers("/hello");,那么当你请求 /hello 接口时就会走到这里来,也就是说这个不经过 Spring Security Filter。
4.如果查询到的 filters 中是有值的,那么这个 filters 集合中存放的就是我们要经过的过滤器链了。此时它会构造出一个虚拟的过滤器链 VirtualFilterChain 出来,并执行其中的 doFilter 方法。

那么接下来我们就来看看 VirtualFilterChain

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.VirtualFilterChain 类中首先声明了 5 个全局属性,originalChain 表示原生的过滤器链,也就是 Web Filter;additionalFilters 表示 Spring Security 中的过滤器链;firewalledRequest 表示当前请求;size 表示过滤器链中过滤器的个数;currentPosition 则是过滤器链遍历时候的下标。
2.doFilter 方法就是 Spring Security 中过滤器挨个执行的过程,如果 currentPosition == size,表示过滤器链已经执行完毕,此时通过调用 originalChain.doFilter 进入到原生过滤链方法中,同时也退出了 Spring Security 过滤器链。否则就从 additionalFilters 取出 Spring Security 过滤器链中的一个个过滤器,挨个调用 doFilter 方法。

最后,FilterChainProxy 中还定义了 FilterChainValidator 接口及其实现:

public interface FilterChainValidator {
	void validate(FilterChainProxy filterChainProxy);
private static class NullFilterChainValidator implements FilterChainValidator {
	@Override
	public void validate(FilterChainProxy filterChainProxy) {

实际上这个实现并未做任何事情。

这就是 FilterChainProxy 中的整个逻辑。

SecurityConfigurer

SecurityConfigurer在 Spring Security 中是一个非常重要的角色。在前面的文章中,松哥曾经多次提到过,Spring Security 过滤器链中的每一个过滤器,都是通过 xxxConfigurer 来进行配置的,而这些 xxxConfigurer 实际上都是 SecurityConfigurer 的实现。

所以我们今天有必要来跟大家把 SecurityConfigurer 从头到尾捋一捋。

SecurityConfigurer

SecurityConfigurer 本身是一个接口,我们来看下:

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
	void init(B builder) throws Exception;
	void configure(B builder) throws Exception;

可以看到,SecurityConfigurer 中主要是两个方法,init 和 configure。

init 就是一个初始化方法。而 configure 则是一个配置方法。这里只是规范了方法的定义,具体的实现则在不同的实现类中。

需要注意的是这两个方法的参数类型都是一个泛型 B,也就是 SecurityBuilder 的子类,关于 SecurityBuilder ,它是用来构建过滤器链的,松哥将在下篇文章中和大家介绍。

SecurityConfigurer 有三个实现类:

SecurityConfigurerAdapter
GlobalAuthenticationConfigurerAdapter
WebSecurityConfigurer
我们分别来看。

SecurityConfigurerAdapter

SecurityConfigurerAdapter 实现了 SecurityConfigurer 接口,我们所使用的大部分的 xxxConfigurer 也都是 SecurityConfigurerAdapter 的子类。

SecurityConfigurerAdapter 在 SecurityConfigurer 的基础上,还扩展出来了几个非常好用的方法,我们一起来看下:

public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
		implements SecurityConfigurer<O, B> {
	private B securityBuilder;
	private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
	public void init(B builder) throws Exception {
	public void configure(B builder) throws Exception {
	public B and() {
		return getBuilder();
	protected final B getBuilder() {
		if (securityBuilder == null) {
			throw new IllegalStateException("securityBuilder cannot be null");
		return securityBuilder;
	@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;
	private static final class CompositeObjectPostProcessor implements
			ObjectPostProcessor<Object> {
		private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();
		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Object postProcess(Object object) {
			for (ObjectPostProcessor opp : 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;
		private boolean addObjectPostProcessor(
				ObjectPostProcessor<?> objectPostProcessor) {
			boolean result = this.postProcessors.add(objectPostProcessor);
			postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
			return result;

1.CompositeObjectPostProcessor 首先一开始声明了一个 CompositeObjectPostProcessor 实例,CompositeObjectPostProcessor 是 ObjectPostProcessor 的一个实现,ObjectPostProcessor 本身是一个后置处理器,该后置处理器默认有两个实现,AutowireBeanFactoryObjectPostProcessor 和 CompositeObjectPostProcessor。其中 AutowireBeanFactoryObjectPostProcessor 主要是利用了 AutowireCapableBeanFactory 对 Bean 进行手动注册,因为在 Spring Security 中,很多对象都是手动 new 出来的,这些 new 出来的对象和容器没有任何关系,利用 AutowireCapableBeanFactory 可以将这些手动 new 出来的对象注入到容器中,而 AutowireBeanFactoryObjectPostProcessor 的主要作用就是完成这件事;CompositeObjectPostProcessor 则是一个复合的对象处理器,里边维护了一个 List 集合,这个 List 集合中,大部分情况下只存储一条数据,那就是 AutowireBeanFactoryObjectPostProcessor,用来完成对象注入到容器的操作,如果用户自己手动调用了 addObjectPostProcessor 方法,那么 CompositeObjectPostProcessor 集合中维护的数据就会多出来一条,在 CompositeObjectPostProcessor#postProcess 方法中,会遍历集合中的所有 ObjectPostProcessor,挨个调用其 postProcess 方法对对象进行后置处理。
2.and 方法,该方法返回值是一个 securityBuilder,securityBuilder 实际上就是 HttpSecurity,我们在 HttpSecurity 中去配置不同的过滤器时,可以使用 and 方法进行链式配置,就是因为这里定义了 and 方法并返回了 securityBuilder 实例。

这便是 SecurityConfigurerAdapter 的主要功能,后面大部分的 xxxConfigurer 都是基于此类来实现的。

GlobalAuthenticationConfigurerAdapter

GlobalAuthenticationConfigurerAdapter 看名字就知道是一个跟全局配置有关的东西,它本身实现了 SecurityConfigurerAdapter 接口,但是并未对方法做具体的实现,只是将泛型具体化了:

@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter implements
		SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
	public void init(AuthenticationManagerBuilder auth) throws Exception {
	public void configure(AuthenticationManagerBuilder auth) throws Exception {

可以看到,SecurityConfigurer 中的泛型,现在明确成了 AuthenticationManager 和 AuthenticationManagerBuilder。所以 GlobalAuthenticationConfigurerAdapter 的实现类将来主要是和配置 AuthenticationManager 有关。当然也包括默认的用户名密码也是由它的实现类来进行配置的。

我们在 Spring Security 中使用的 AuthenticationManager 其实可以分为两种,一种是局部的,另一种是全局的,这里主要是全局的配置。

WebSecurityConfigurer

还有一个实现类就是 WebSecurityConfigurer,这个可能有的小伙伴比较陌生,其实他就是我们天天用的 WebSecurityConfigurerAdapter 的父接口。

所以 WebSecurityConfigurer 的作用就很明确了,用户扩展用户自定义的配置。

SecurityConfigurer 默认主要是这三个实现,考虑到大多数的过滤器配置都是通过 SecurityConfigurerAdapter 进行扩展的,因此我们今天就通过这条线进行展开。

SecurityConfigurerAdapter

SecurityConfigurerAdapter 的实现主要也是三大类:

UserDetailsAwareConfigurer
AbstractHttpConfigurer
LdapAuthenticationProviderConfigurer

考虑到 LDAP 现在使用很少,所以这里我来和大家重点介绍下前两个。

UserDetailsAwareConfigurer

这个配置类看名字大概就知道这是用来配置用户类的

AbstractDaoAuthenticationConfigurer

AbstractDaoAuthenticationConfigurer 中所做的事情比较简单,主要是构造了一个默认的 DaoAuthenticationProvider,并为其配置 PasswordEncoder 和 UserDetailsService。

UserDetailsServiceConfigurer

UserDetailsServiceConfigurer 重写了 AbstractDaoAuthenticationConfigurer 中的 configure 方法,在 configure 方法执行之前加入了 initUserDetailsService 方法,以方便开发展按照自己的方式去初始化 UserDetailsService。不过这里的 initUserDetailsService 方法是空方法。

UserDetailsManagerConfigurer

UserDetailsManagerConfigurer 中实现了 UserDetailsServiceConfigurer 中定义的 initUserDetailsService 方法,具体的实现逻辑就是将 UserDetailsBuilder 所构建出来的 UserDetails 以及提前准备好的 UserDetails 中的用户存储到 UserDetailsService 中。

该类同时添加了 withUser 方法用来添加用户,同时还增加了一个 UserDetailsBuilder 用来构建用户,这些逻辑都比较简单,小伙伴们可以自行查看。

JdbcUserDetailsManagerConfigurer

JdbcUserDetailsManagerConfigurer 在父类的基础上补充了 DataSource 对象,同时还提供了相应的数据库查询方法。

InMemoryUserDetailsManagerConfigurer

InMemoryUserDetailsManagerConfigurer 在父类的基础上重写了构造方法,将父类中的 UserDetailsService 实例定义为 InMemoryUserDetailsManager。

DaoAuthenticationConfigurer

DaoAuthenticationConfigurer 继承自 AbstractDaoAuthenticationConfigurer,只是在构造方法中修改了一下 userDetailsService 而已。

有小伙伴可能要问了,JdbcUserDetailsManagerConfigurer 或者 InMemoryUserDetailsManagerConfigurer,到底在哪里可以用到呀?

松哥给大家举一个简单的例子:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("javaboy")
                .password("{noop}123")
                .roles("admin");
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()

当你调用 auth.inMemoryAuthentication 进行配置时,实际上调用的就是 InMemoryUserDetailsManagerConfigurer。

这下明白了吧!

AbstractHttpConfigurer

AbstractHttpConfigurer 这一派中的东西非常多,我们所有的过滤器配置,都是它的子类,我们来看下都有哪些类?

可以看到,它的实现类还是非常多的。

这么多实现类,松哥就不一一给大家介绍了,我挑一个常用的 FormLoginConfigurer 来给大家详细介绍,只要大家把这个理解了,其他的照猫画虎就很好理解了。

我们一个一个来看。

AbstractHttpConfigurer

AbstractHttpConfigurer 继承自 SecurityConfigurerAdapter,并增加了两个方法,disable 和 withObjectPostProcessor:

public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
		extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
	 * Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh
	 * version of the configuration can be applied.
	 * @return the {@link HttpSecurityBuilder} for additional customizations
	@SuppressWarnings("unchecked")
	public B disable() {
		getBuilder().removeConfigurer(getClass());
		return getBuilder();
	@SuppressWarnings("unchecked")
	public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
		addObjectPostProcessor(objectPostProcessor);
		return (T) this;

这两个方法松哥之前都有给大家介绍过,disable 基本上是大家的老熟人了,我们常用的 .csrf().disable() 就是出自这里,那么从这里我们也可以看到 disable 的实现原理,就是从 getBuilder 中移除相关的 xxxConfigurer,getBuilder 方法获取到的实际上就是 HttpSecurity,所以移除掉 xxxConfigurer 实际上就是从过滤器链中移除掉某一个过滤器,例如 .csrf().disable() 就是移除掉处理 csrf 的过滤器。

另一个增加的方法是 withObjectPostProcessor,这是为配置类添加手动添加后置处理器的。在 AbstractHttpConfigurer 的父类中其实有一个类似的方法就是 addObjectPostProcessor,但是 addObjectPostProcessor 只是一个添加方法,返回值为 void,而 withObjectPostProcessor 的返回值是当前配置类,也就是 xxxConfigurer,所以如果使用 withObjectPostProcessor 的话,可以使用链式配置,事实上,在松哥之前的文章,以及 vhr(https://github.com/lenve/vhr) 项目中,使用的也都是 withObjectPostProcessor 方法(当然,你也可以使用 addObjectPostProcessor,最终效果是一样的)。

AbstractAuthenticationFilterConfigurer

AbstractAuthenticationFilterConfigurer 类的功能比较多,源码也是相当相当长。不过我们只需要抓住两点即可,init 方法和 configure 方法,因为这两个方法是所有 xxxConfigurer 的灵魂。

@Override
public void init(B http) throws Exception {
	updateAuthenticationDefaults();
	updateAccessDefaults(http);
	registerDefaultAuthenticationEntryPoint(http);

init 方法主要干了三件事:

updateAuthenticationDefaults 主要是配置了登录处理地址,失败跳转地址,注销成功跳转地址。
updateAccessDefaults 方法主要是对 loginPage、loginProcessingUrl、failureUrl 进行 permitAll 设置(如果用户配置了 permitAll 的话)。
registerDefaultAuthenticationEntryPoint 则是注册异常的处理器。
再来看 configure 方法:

@Override
public void configure(B http) throws Exception {
	PortMapper portMapper = http.getSharedObject(PortMapper.class);
	if (portMapper != null) {
		authenticationEntryPoint.setPortMapper(portMapper);
	RequestCache requestCache = http.getSharedObject(RequestCache.class);
	if (requestCache != null) {
		this.defaultSuccessHandler.setRequestCache(requestCache);
	authFilter.setAuthenticationManager(http
			.getSharedObject(AuthenticationManager.class));
	authFilter.setAuthenticationSuccessHandler(successHandler);
	authFilter.setAuthenticationFailureHandler(failureHandler);
	if (authenticationDetailsSource != null) {
		authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
	SessionAuthenticationStrategy sessionAuthenticationStrategy = http
			.getSharedObject(SessionAuthenticationStrategy.class);
	if (sessionAuthenticationStrategy != null) {
		authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
	RememberMeServices rememberMeServices = http
			.getSharedObject(RememberMeServices.class);
	if (rememberMeServices != null) {
		authFilter.setRememberMeServices(rememberMeServices);
	F filter = postProcess(authFilter);
	http.addFilter(filter);

configure 中的逻辑就很简答了,构建各种各样的回调函数设置给 authFilter,authFilter 再去 postProcess 中走一圈注册到 Spring 容器中,最后再把 authFilter 添加到过滤器链中。

这便是 AbstractAuthenticationFilterConfigurer 的主要功能。需要提醒大家的是,我们日常配置的,如:

loginPage
loginProcessingUrl
permitAll
defaultSuccessUrl
failureUrl
等方法都是在这里定义的。

最后我们再来看看 FormLoginConfigurer。

FormLoginConfigurer

FormLoginConfigurer 在定义是,明确了 AbstractAuthenticationFilterConfigurer 中的泛型是 UsernamePasswordAuthenticationFilter,也就是我们这里最终要配置的过滤是 UsernamePasswordAuthenticationFilter。

FormLoginConfigurer 重写了 init 方法,配置了一下默认的登录页面。其他的基本上都是从父类来的,未做太多改变。

另外我们日常配置的很多东西也是来自这里:

好啦,这就是 FormLoginConfigurer 这个配置类,FormLoginConfigurer 对应的过滤器是 UsernamePasswordAuthenticationFilter,小伙伴们可以自行分析其他的 xxxConfigurer,每一个 xxxConfigurer 都对应了一个 不同的 Filter。

我有一壶酒 足以慰风尘 尽倾江海里 赠饮天下人