[SpringSecurity5.6.2源码分析六]:ChannelProcessingFilter

1、基础用法

  • ChannelProcessingFilter是SpringSecurity的第一个过滤器,具体排序规则见FilterComparator,

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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值