盘点认证框架 : SpringSecurity Filter 篇

1,826 阅读10分钟

总文档 :文章目录
Github : github.com/black-ant

一 . 前言

上一篇聊了聊 Secutity 的基础 , 这一篇我们聊一聊 Securiy Filter , 基本上 Security 常见得功能都能通过 Filter 找到相关的痕迹 .

二 . 一个基本的 Filter 案例

先来看看我们之前这么注册 Filter 的 >>>

AbstractAuthenticationProcessingFilter filter = new DatabaseAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);

我们来追溯一下 Filter 怎么被加载进去的 :

Step First : 将 Filter 加载到 Security 体系中

// 添加到 HttpSecurity
C- HttpSecurity
    - this.filters.add(filter) : 这里的 filters 仅仅是一个 List 集合
   
// 追溯代码可以看到这个集合会用于创建一个 DefaultSecurityFilterChain    
protected DefaultSecurityFilterChain performBuild() throws Exception {
	Collections.sort(this.filters, this.comparator);
	return new DefaultSecurityFilterChain(this.requestMatcher, this.filters);
}    

HttpSecurity 这个类我们后面会详细说说 , 这里了解到他其中维护了一个 Filter 集合即可 , 这个集合会被加载到 FilterChain 中

Step 2 : Filter Chain 的使用方式

// 成功标注断点后 , 可以追溯到整个的加载流程 : 
// Step 1 : 要构建一个 springSecurityFilterChain
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain()throws Exception {
	boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
	if (!hasConfigurers) {
		WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});
		webSecurity.apply(adapter);
	}
	return webSecurity.build();
}

// Step 2 : webSecurity.build() 执行构建
public final O build() throws Exception {
    // 居然还可以看到 CAS 操作 , 这里应该是设置绑定状态
	if (this.building.compareAndSet(false, true)) {
        // 执行 Build
		this.object = doBuild();
		return this.object;
	}
	throw new AlreadyBuiltException("This object has already been built");
}

// Step 3 : AbstractConfiguredSecurityBuilder 中
protected final O doBuild() throws Exception {
	synchronized (configurers) {
		buildState = BuildState.INITIALIZING;
                // 为子类挂载钩子
		beforeInit();
		init();

		buildState = BuildState.CONFIGURING;
                // 在调用每个SecurityConfigurer#configure(SecurityBuilder)方法之前调用。
                // 子类可以在不需要使用SecurityConfigurer时 , 覆盖这个方法来挂载到生命周期中
		beforeConfigure();
		configure();

		buildState = BuildState.BUILDING;
		// 实际构建对象
		O result = performBuild();

		buildState = BuildState.BUILT;

		return result;
	}
}


// Step 4 : 至此反射获取到 Filters 链
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
	Collections.sort(filters, comparator);
	return new DefaultSecurityFilterChain(requestMatcher, filters);
}


image.png

可以看到 , 最开始添加到 Filter 集合的 Filter ,最终会用于构建 springSecurityFilterChain , 那么 springSecurityFilterChain 又是干什么的呢?

三 . Security Filter Chain

Security 的 Filter 和 WebFilter 本质是一样的 , 只是为了实现 Seccurity 的功能

>>> 来看一下 FilterChain 的调用链 :
FilterChain001.jpg

image.png

3.1 FilterChain 的创建过程

FilterChain 的核心是一个 VirtualFilterChain , 每个请求过来都会有一个VirtualFilterChain 生成

VirtualFilterChain 是 FilterChainProxy 的内部类.

// 补充 : VirtualFilterChain : 内部过滤器链实现,用于通过与请求匹配的额外内部过滤器列表传递请求
C- VirtualFilterChain
    ?- 每次运行的时候都会创建 , 来链表调用所有的 Filter
    P- currentPosition : 当前运行 Filter 的下标
    P- FirewalledRequest :  可用于拒绝潜在危险的请求和/或包装它们来控制它们的行为
    P- List<Filter> additionalFilters : 包含所有的 Filter 对象
    M- doFilter(ServletRequest request, ServletResponse response)
    ?- 这个方法会从2个维度来处理
        1- currentPosition == size : 当执行最后一个的时候 , 先重置 FirewalledRequest , 再调用 originalChain
        2- currentPosition != size  : 在此之前依次执行 Filter 集合中的 doFilter
        

VirtualFilterChain 的创建流程 :


C- FilterChainProxy extends GenericFilterBean
	?- GenericFilterBean 继承了 Filter 接口 , 其最终会由 SpringWeb 的 Filter 进行调用
	M- doFilterInternal
		- FirewalledRequest 的相关处理
		- 创建了一个 VirtualFilterChain ,执行 Filter 链

// implements Filter
@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);
	}
}



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) {
			// 日志略...	
			fwRequest.reset();
			chain.doFilter(fwRequest, fwResponse);
			return;
	}
        // 这里创建了一个内部类 VirtualFilterChain
	VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
        // 类似于链表的方式 , 依次来调用
	vfc.doFilter(fwRequest, fwResponse);
}

// 扩展 : FirewalledRequest 
C- FirewalledRequest : 
	?- 这是一个配合防火墙功能的Request 实现类 , 其通常配合 HttpFirewall 来实现
	M- reset : 重置方法,该方法允许在请求离开安全过滤器链时由FilterChainProxy重置部分或全部状态
    
C- StrictHttpFirewall
    M- getFirewalledRequest
        - rejectForbiddenHttpMethod(request) : 拒绝禁止的 HttpMethod
        - rejectedBlacklistedUrls(request) : 拒绝黑名单 URL 
        - return new FirewalledRequest(request) : 这里创建了一个 FirewalledRequest , 不过 reset 是空实现

可以看到 , 这里每次执行 doFilterInternal 时都会创建一个 VirtualFilterChain .

主要抽象类 AbstractAuthenticationProcessingFilter

C- AbstractAuthenticationProcessingFilter 
	- !requiresAuthentication(request, response) : 确定是否匹配该请求
		?- 注意 ,我们构建 DatabaseAuthenticationFilter 的时候其实是传入了一个Matcher匹配器的
		- requiresAuthenticationRequestMatcher.matches(request);
		- 不匹配则继续执行 FilterChain
	- 匹配后继续执行
	- Authentication authResult = attemptAuthentication(request, response);
	// 这个方法是需要实现类复写的 , 在实现类中我们做了下面的事情
		- 将 Request 中的验证信息 (账户密码, 如果需要扩展 ,可以是更多信息 , Cookie , Header 等等) 取出
		- 构建了一个 Token (DatabaseUserToken) 
		- 将 Token 放入 Details 中
		- 通过 AuthenticationManager 调用 ProviderManager 完成认证
		// 具体的认证方式我们后续在详细分析

我之前以为 Security 的方式是把 所有的 Filter 走一遍后再执行 Provider , 从这里看来他采用的是Filter 适配后就直接执行 Provider

3.2 WebAsyncManagerIntegrationFilter


C- WebAsyncManagerIntegrationFilter
	?- 提供SecurityContext和Spring Web的webbasyncmanager之间的集成            
	?- SecurityContextCallableProcessingInterceptor#beforeConcurrentHandling 用于填充SecurityContext
        
C- 创建一个WebAsyncManager  
	?- 用于管理异步请求处理的中心类,主要用作SPI,通常不直接由应用程序类使用。
        
@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {
    // 创建一个WebAsyncManager
    // 用于管理异步请求处理的中心类,主要用作SPI,通常不直接由应用程序类使用。 
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
    SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager
				.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
    // 如果没有 SecurityContextCallableProcessingInterceptor , 则创建一个注入 WebAsyncManager    
    if (securityProcessingInterceptor == null) {
        asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY,new SecurityContextCallableProcessingInterceptor());
    }

    filterChain.doFilter(request, response);
}
        
        

3.3 SecurityContextPersistenceFilter 体系

注意 , SecurityContext 是整个认证的核心 , 拥有 SecurityContext 即表示认证成功

C- SecurityContextPersistenceFilter
	?- 这是一个必选的Filter , 其目的是为了往 SecurityContextHolder 中插入一个 SecurityContext , SecurityContext 是最核心的认证容器
	- SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
            ?- 注意 , 这里会尝试获取Sesssion 的 Context , 用于验证
	- SecurityContextHolder.setContext(contextBeforeChainExecution)
            ?- 设置一个 Context
	- chain.doFilter(holder.getRequest(), holder.getResponse());
	- finally 中会在所有filter 完成后 , 往 SecurityContextHolder 插入一个 contextAfterChainExecution
        ?- 注意前面是 contextBeforeChainExecution 
        
// finally 代码一览
finally {
    SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
    // 清除 Context
    SecurityContextHolder.clearContext();
    // 重新保存新得 Context
    repo.saveContext(contextAfterChainExecution, holder.getRequest(),holder.getResponse());
    request.removeAttribute(FILTER_APPLIED);
}

        
        

3.4 HeaderWriterFilter 体系

List<HeaderWriter> headerWriters : 构造对象的时候会写入一个list

核心方法 : doFilterInternal

  • 准备了2个 HeaderWriterResponse , HeaderWriterRequest , 他们支持对Header 的二次封装 (原本的 Servlet 是不支持的)
  • filterChain 完成后会执行 headerWriterResponse.writeHeaders();
// eaderWriterResponse.writeHeaders() 是从 HeadersConfigurer 中获取的
C- HeadersConfigurer        
    M- private List<HeaderWriter> getHeaderWriters() {
        List<HeaderWriter> writers = new ArrayList<>();
        addIfNotNull(writers, contentTypeOptions.writer);
        addIfNotNull(writers, xssProtection.writer);
        addIfNotNull(writers, cacheControl.writer);
        addIfNotNull(writers, hsts.writer);
        addIfNotNull(writers, frameOptions.writer);
        addIfNotNull(writers, hpkp.writer);
        addIfNotNull(writers, contentSecurityPolicy.writer);
        addIfNotNull(writers, referrerPolicy.writer);
        addIfNotNull(writers, featurePolicy.writer);
        writers.addAll(headerWriters);
        return writers;
}

3.5 LogoutFilter

LogoutFilter 允许定制LogoutHandler , 这一点在构造函数里面就能看到 可以看到 , 默认使用 logout 地址作为拦截请求

public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler,
			LogoutHandler... handlers) {
		this.handler = new CompositeLogoutHandler(handlers);
		Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null");
		this.logoutSuccessHandler = logoutSuccessHandler;
		setFilterProcessesUrl("/logout");
}


// LogoutFilter doFilter 逻辑
M- doFilter 
    ?- 只要的操作就是调用handler 执行 logout 逻辑 , 并且调用 LogoutSuccess 逻辑 
    
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
    
    if (requiresLogout(request, response)) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // 执行 LogoutHandler 的实现类
        this.handler.logout(request, response, auth);
        // 执行 LogoutSuccessHandler 的实现类
        logoutSuccessHandler.onLogoutSuccess(request, response, auth);
        return;
    }

    chain.doFilter(request, response);
}    

// PS : 这个 Filter 有一定局限性 , 无法处理多个 Handler , 可以考虑定制一个 Filter
// Handler 实现类我们后续再深入


3.6 CsrfFilter

之前了解到 , 为了使同步器令牌模式能够防止 CSRF 攻击,必须在 HTTP 请求中包含实际的 CSRF 令牌。这必须包含在浏览器不会自动包含在 HTTP 请求中的请求的一部分(即表单参数、 HTTP 头等)中。

Spring Security 的 CsrfFilter 将一个 CsrfToken 作为一个名为 _csrf 的 HttpServletRequest 公开属性


protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
					throws ServletException, IOException {
    request.setAttribute(HttpServletResponse.class.getName(), response);
    // 加载 CsrfToken
    CsrfToken csrfToken = this.tokenRepository.loadToken(request);
    final boolean missingToken = csrfToken == null;
    if (missingToken) {
        // 缺失则重新创建一个
        csrfToken = this.tokenRepository.generateToken(request);
        this.tokenRepository.saveToken(csrfToken, request, response);
    }
    request.setAttribute(CsrfToken.class.getName(), csrfToken);
    request.setAttribute(csrfToken.getParameterName(), csrfToken);

    if (!this.requireCsrfProtectionMatcher.matches(request)) {
        filterChain.doFilter(request, response);
        return;
    }

    // 跨域后比对实际Token
    String actualToken = request.getHeader(csrfToken.getHeaderName());
    if (actualToken == null) {
        actualToken = request.getParameter(csrfToken.getParameterName());
    }
    if (!csrfToken.getToken().equals(actualToken)) {
        if (missingToken) {
            this.accessDeniedHandler.handle(request, response,new MissingCsrfTokenException(actualToken));
        } else {
            this.accessDeniedHandler.handle(request, response,new InvalidCsrfTokenException(csrfToken, actualToken));
        }
        return;
    }

    filterChain.doFilter(request, response);
}

3.9 其他零散Token

如果缓存的请求与当前请求匹配,则负责重新构造已保存的请求

整个核心代码主要是 2句话:其中主要是封装了一个新得 wrappedSavedRequest

HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest(
    (HttpServletRequest) request, (HttpServletResponse) response);
chain.doFilter(wrappedSavedRequest == null ? request : wrappedSavedRequest,response);

SecurityContextHolderAwareRequestFilter 一个过滤器,它使用实现servlet API安全方法的请求包装器填充ServletRequest
简单点说 , 就是一个封装 Request 的 Filter , 封装的 HttpServletRequest 提供了很多额外的功能

  • HttpServletRequest.authenticate() - 允许用户确定他们是否被验证,如果没有,则将用户发送到登录页面
  • HttpServletRequest.login() - 允许用户使用AuthenticationManager进行身份验证
  • HttpServletRequest.logout() - 允许用户使用Spring Security中配置的LogoutHandlers注销

SessionManagementFilter SessionManagementFilter 中提供了多个对象用于在用户已经认证后进行 Session 会话活动 ,** 激活会话固定保护机制或检查多个并发登录**

  • SecurityContextRepository securityContextRepository;
  • SessionAuthenticationStrategy sessionAuthenticationStrategy;

3.8 ExceptionTranslationFilter

作用 : 处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException。

如果检测到AuthenticationException,过滤器将启动authenticationEntryPoint。这允许通用地处理来自AbstractSecurityInterceptor的任何子类的身份验证失败。

sendStartAuthentication(request, response, chain,(AuthenticationException) exception);

如果检测到AccessDeniedException,筛选器将确定该用户是否是匿名用户

  • 如果它们是匿名用户,则将启动authenticationEntryPoint。
  • 如果它们不是匿名用户,则筛选器将委托给AccessDeniedHandler。
  • 默认情况下,过滤器将使用AccessDeniedHandlerImpl。
sendStartAuthentication(request,response,chain,new InsufficientAuthenticationException(
    messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication","Full authentication is required to access this resource")));

(PS : 因为是链式结构 , 所以他作为最后一个 , 也是处在最外层的)

核心是通过一个 catch 来处理

try {
    chain.doFilter(request, response);
}catch (Exception ex) {
    Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
    RuntimeException ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
    // 获取依次类型
    if (ase == null) {
        ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
            AccessDeniedException.class, causeChain);
    }

    if (ase != null) {
        if (response.isCommitted()) {
            throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", ex);
        }
        // 专属处理 SpringSecurityException
        handleSpringSecurityException(request, response, chain, ase);
    }else {
        if (ex instanceof ServletException) {
            throw (ServletException) ex;
        }else if (ex instanceof RuntimeException) {
            throw (RuntimeException) ex;
        }
        throw new RuntimeException(ex);
    }
}
  • 首先,ExceptionTranslationFilter 调用 FilterChain.doFilter (请求、响应)来调用应用程序的其余部分。
  • 如果用户没有经过身份验证,或者它是 AuthenticationException,那么启动身份验证。
    • 清空 Security contextholder
    • 将 HttpServletRequest 保存在 RequestCache 中。当用户成功进行身份验证时,使用 RequestCache获取原始请求
    • AuthenticationEntryPoint 用于从客户机请求凭据。
      • 例如,它可能会重定向到一个登录页面,或者发送一个 WWW-Authenticate 标头。
  • 否则,如果它是一个 AccessDeniedException,那么 Access Denied

image.png

如果应用程序没有抛出 AccessDeniedException 或 AuthenticationException,那么 ExceptionTranslationFilter 不会做任何事情。

四 . 业务流

谈到了 Filter , 肯定就要细聊 Filter 对应的业务 , 上面说了一些简单的 Filter 业务 , 这一段我们来说一说比较大的业务流程 :

4.1 HttpSecurity 的业务匹配

我们在配置 Security 的时候 , 一般都会配置 Request Match 等参数 , 例如 :

 http.authorizeRequests()
                .antMatchers("/test/**").permitAll()
                .antMatchers("/before/**").permitAll()
                .antMatchers("/index").permitAll()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()                      //其它请求都需要校验才能访问
                .and()
                .formLogin()
                .loginPage("/login")                             //定义登录的页面"/login",允许访问
                .defaultSuccessUrl("/home")  //登录成功后默认跳转到"list"
                .successHandler(myAuthenticationSuccessHandler).failureHandler(myAuthenctiationFailureHandler).permitAll().and()
                .logout()                                           //默认的"/logout", 允许访问
                .logoutSuccessUrl("/index")
                .permitAll();

那么这些参数是怎么生效的呢 ?

Step End : 最终匹配对象

我们来根据整个业务流程逆推 , 其最终对象是一个 RequestMatcher 实现类

注意 , 我们其上的 antMatchers 类型会生成多种不同的实现类 :

  • AndRequestMatcher : 请求路径
  • IpAddressMatcher : IP地址
  • MediaTypeRequestMatcher : 媒体类型

.... 等等其他的就不详细说了

拿到实现类后 ,调用 实现类的matches 方法返回最终结果

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

Step Start : 看看请求的起点

找到了最终的匹配点 , 后面就好说了 , 打个断点 , 整个调用链就清清楚楚了



// Step1 : FilterChainProxy
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);
    
    //...... 省略
    // 执行 Filter 链 , 如果没有相当于直接进去
    if (filters == null || filters.size() == 0) {
        chain.doFilter(fwRequest, fwResponse);
    }

// Step2 : getFilters 过滤 Filter Chain 
for (SecurityFilterChain chain : filterChains) {
    // 如果地址匹配 , 则执行对象 Filter 链
    if (chain.matches(request)) {
        return chain.getFilters();
    }
}

这里可以看到 , 如果没有被拦截成功的 ,最终应该就直接运行了 , 所以 Security 一切的起点都是 Filter

五.补充

5.1 DelegatingFilterProxy 补充

Security 通过 DelegatingFilterProxy 将 Security 融入到 WebFilter 的体系中 ,其主要流程为 :

  • C- DelegatingFilterProxy # doFilter
  • C- DelegatingFilterProxy # invokeDelegate
  • C- FilterChainProxy # doFilter

image.png

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

    // ->  PIC51001 : delegate 对象结构
    Filter delegateToUse = this.delegate;
    if (delegateToUse == null) {
        synchronized (this.delegateMonitor) {
            delegateToUse = this.delegate;
            if (delegateToUse == null) {
                WebApplicationContext wac = findWebApplicationContext();
                if (wac == null) {
                    throw new IllegalStateException("...");
                }
                delegateToUse = initDelegate(wac);
            }
            this.delegate = delegateToUse;
        }
    }

    // Let the delegate perform the actual doFilter operation.
    invokeDelegate(delegateToUse, request, response, filterChain);
}



protected void invokeDelegate(
      Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
    
   // 会调用到 FilterChainProxy , 正式进入 Security 体系
   delegate.doFilter(request, response, filterChain);
}

PIC51001 : delegate 对象结构

image.png

继续补充 : delegate 的初始化 , 获取 FilterChain

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
   String targetBeanName = getTargetBeanName();
   // 这里的 TargetBeanName 为 springSecurityFilterChain
   Filter delegate = wac.getBean(targetBeanName, Filter.class);
   if (isTargetFilterLifecycle()) {
      delegate.init(getFilterConfig());
   }
   return delegate;
}

总结

Spring Security 的起点就是 Filter ,常用的功能都能通过 Filter 找到相关的痕迹 , 后续我们会继续分析更底层的东西, 来开枝散叶的看看底下经历了什么

Security 的 Filter 和 WebFilter 的本质一样 , Security 通过一个 DelegatingFilterProxySecurityFilterChain 集中到 Filter 体系中

FilterChain 的核心是一个 VirtualFilterChain , 每个请求过来都会有一个VirtualFilterChain 生成 ,其中会添加所有的 Filter 类

的 Filter 包括 :

  • SecurityContextPersistenceFilter : 对 SecurityContext 进行持久化操作
  • HeaderWriterFilter : 对 Header 进行二次处理 , 因为很多认证信息会放在 Header 中 , 这也是一个极其重要的类
  • LogoutFilter : 拦截 logout 请求 , 并且退出
  • ExceptionTranslationFilter : 对流程中的异常进行处理 (AccessDeniedException和AuthenticationException)

Filter 会通过 matches 进行拦截 , 判断是否要执行 Filters 逻辑

更新日志

V20210803 : 补充 DelegatingFilterProxy 逻辑