1、基础用法
- • ChannelProcessingFilter是SpringSecurity的第一个过滤器,具体排序规则见FilterComparator,
 
![640?wx_fmt=png [SpringSecurity5.6.2源码分析六]:ChannelProcessingFilter_源码](https://file.cfanz.cn/uploads/png/2023/09/11/15/QEE09AI546.png)
image.png
- • 主要作用:可限制服务端接受的安全协议,比如说仅支持Https或者Http
 
1.1 开启配置类:
- • 首先我们注册到容器中的WebSecurityConfigurerAdapter是针对于WebSecurity的配置类,但是像Cors、Csrf等等功能是靠HttpSecurity配置的, 我们可以通过重写此方法,并通过requiresChannel()注册一个ChannelSecurityConfigurer,这是用于配置通道的配置类
 
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ......
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requiresChannel();
   }
   ......
}1.2 使用
- • 注册通道处理器(ChannelProcessor)
 
http.requiresChannel().channelProcessors(list)- • 通道处理器是用于用于判断是否满足某些安全条件,比如说
 
- • InsecureChannelProcessor:要求请求使用非安全通道
 - • SecureChannelProcessor:要求请求使用安全通道
 
- • 针对于哪些请求需要进行安全通道的限制
 
http.requiresChannel().mvcMatchers("/*").requires("REQUIRES_SECURE_CHANNEL")
http.requiresChannel().mvcMatchers("/*").requires("REQUIRES_INSECURE_CHANNEL")- • 注册一个ObjectPostProcessor,让过滤器在创建完成后执行的回调方法
 
http.requiresChannel().withObjectPostProcessor(new ObjectPostProcessor<ChannelProcessingFilter>() {
   @Override
   public <O extends ChannelProcessingFilter> O postProcess(O object) {
      return object;
   }
});- • 这并不是当前介绍的这个过滤器独有,而应该是大部分过滤器都应该具备的,比如说ExceptionTranslationFilter,BasicAuthenticationFilter等等过滤器都具备的,都是在对应的配置类中负责执行的
 
2、ChannelSecurityConfigurer
- • 所有的配置类都是基于建造者模式进行的
 
@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;
  }
}- • ChannelSecurityConfigurer只重写了configure方法,这里也只讲这个,这里出现了两个新的类
 
- • ChannelDecisionManagerImpl:负责管理注册的ChannelProcessor
 - • DefaultFilterInvocationSecurityMetadataSource:主要是根据请求确定本次请求需要具备哪些安全属性或者说权限
 
@Override
public void configure(H http) {
   ChannelDecisionManagerImpl channelDecisionManager = new ChannelDecisionManagerImpl();
   //设置通道处理器
   channelDecisionManager.setChannelProcessors(getChannelProcessors(http));
   channelDecisionManager = postProcess(channelDecisionManager);
   //设置通道决策管理器
   this.channelFilter.setChannelDecisionManager(channelDecisionManager);
   //设置安全元数据源
   DefaultFilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource = new DefaultFilterInvocationSecurityMetadataSource(
         this.requestMap);
   this.channelFilter.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
   this.channelFilter = postProcess(this.channelFilter);
   http.addFilter(this.channelFilter);
}- • 第6行和14行代码就是要执行注册的ObjectPostProcessor
 - • 接下来看getChannelProcessors(http)方法
 
/**
 * 获得通道处理器
 * @param http
 * @return
 */
private List<ChannelProcessor> getChannelProcessors(H http) {
   if (this.channelProcessors != null) {
      return this.channelProcessors;
   }
   //创建安全通过处理器
   InsecureChannelProcessor insecureChannelProcessor = new InsecureChannelProcessor();
   SecureChannelProcessor secureChannelProcessor = new SecureChannelProcessor();
   PortMapper portMapper = http.getSharedObject(PortMapper.class);
   if (portMapper != null) {
      RetryWithHttpEntryPoint httpEntryPoint = new RetryWithHttpEntryPoint();
      httpEntryPoint.setPortMapper(portMapper);
      insecureChannelProcessor.setEntryPoint(httpEntryPoint);
      //创建不安全通过处理器
      RetryWithHttpsEntryPoint httpsEntryPoint = new RetryWithHttpsEntryPoint();
      httpsEntryPoint.setPortMapper(portMapper);
      secureChannelProcessor.setEntryPoint(httpsEntryPoint);
   }
   insecureChannelProcessor = postProcess(insecureChannelProcessor);
   secureChannelProcessor = postProcess(secureChannelProcessor);
   return Arrays.asList(insecureChannelProcessor, secureChannelProcessor);
}- • 可以看到当我们没有为其注册通道处理器的时候,默认是注册两个,一个是要求安全的一个是非安全的
 - • 这里出现了两个新类:PortMapper和RetryWithHttpEntryPoint
 - • PortMapper:如果是系统要求使用安全的也就是Https那么就需要进行重定向,但是重定向后的端口怎么确定呢,就是靠这个类
 
- • 可以看出默认就只有两个端口的转换
 
public PortMapperImpl() {
   this.httpsPortMappings = new HashMap<>();
   this.httpsPortMappings.put(80, 443);
   this.httpsPortMappings.put(8080, 8443);
}- • RetryWithHttpEntryPoint:当请求的安全协议不支持的时候,进行重定向操作
 
@Override
public void commence(HttpServletRequest request, HttpServletResponse response) throws IOException {
   String queryString = request.getQueryString();
   String redirectUrl = request.getRequestURI() + ((queryString != null) ? ("?" + queryString) : "");
   Integer currentPort = this.portResolver.getServerPort(request);
   Integer redirectPort = getMappedPort(currentPort);
   if (redirectPort != null) {
      // http和https默认的端口不需要设置
      boolean includePort = redirectPort != this.standardPort;
      String port = (includePort) ? (":" + redirectPort) : "";
      redirectUrl = this.scheme + request.getServerName() + port + redirectUrl;
   }
   this.logger.debug(LogMessage.format("Redirecting to: %s", redirectUrl));
   // 设置重定向Url
   this.redirectStrategy.sendRedirect(request, response, redirectUrl);
}3、ChannelProcessingFilter
- • 此过滤器就两个方法:afterPropertiesSet() 和 doFilter(......)
 - • afterPropertiesSet():检查注册的安全属性是否合法
 
@Override
public void afterPropertiesSet() {
   ......
   // 获取所有安全属性
   Collection<ConfigAttribute> attrDefs = this.securityMetadataSource
         .getAllConfigAttributes();
   if (attrDefs == null) {
      if (this.logger.isWarnEnabled()) {
         this.logger
               .warn("Could not validate configuration attributes as the FilterInvocationSecurityMetadataSource did "
                     + "not return any attributes");
      }
      return;
   }
   Set<ConfigAttribute> unsupportedAttributes = new HashSet<>();
   // 判断通道管理器中的通道处理器是否支持对于的安全属性(权限)
   for (ConfigAttribute attr : attrDefs) {
      if (!this.channelDecisionManager.supports(attr)) {
         unsupportedAttributes.add(attr);
      }
   }
   // 不为空就说明有安全属性不支持直接抛出异常
   if (unsupportedAttributes.size() == 0) {
      if (this.logger.isInfoEnabled()) {
         this.logger.info("Validated configuration attributes");
      }
   }
   else {
      throw new IllegalArgumentException(
            "Unsupported configuration attributes: " + unsupportedAttributes);
   }
}- • doFilter(......):
 
- • 第一步:通过安全元数据源获得接口所需权限
 - • 第二步:调用通道决策管理器判断请求是否合法,如果不合法就会在内部设置重定向的参数,然后这里就会直接返回了
 
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
   //包装请求
   HttpServletRequest request = (HttpServletRequest) req;
   HttpServletResponse response = (HttpServletResponse) res;
   FilterInvocation filterInvocation = new FilterInvocation(request, response, chain);
   //通过安全元数据源获得接口所需权限
   Collection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(filterInvocation);
   if (attributes != null) {
      this.logger.debug(LogMessage.format("Request: %s; ConfigAttributes: %s", filterInvocation, attributes));
      //调用通道决策管理器
      this.channelDecisionManager.decide(filterInvocation, attributes);
      //是否已经完成
      if (filterInvocation.getResponse().isCommitted()) {
         return;
      }
   }
   chain.doFilter(request, response);
}









