首页 > 编程语言 >[SpringSecurity5.6.2源码分析十一]:CorsFilter

[SpringSecurity5.6.2源码分析十一]:CorsFilter

时间:2023-09-18 18:34:55浏览次数:55  
标签:SpringSecurity5.6 return 请求 request CorsFilter 源码 response 跨域

前言

  • • 跨域:两个域名的(协议、域名/ip、端口)有任意一个不同即视为跨域
  • • 跨域资源共享(Cors):即浏览器允许访问其他跨域的资源
  • • 而CorsFilter就是SpringSecurity用来处理Cors的过滤器

1. CorsConfigurer

  • • CorsConfigurer是CorsFilter对应的配置类,其中就只有一个重要方法
  • • configure(...)

1.1 configure(...)

  • • configure(...)源码很简单,主要是调用了getCorsFilter(...)方法
@Override
public void configure(H http) {
   ApplicationContext context = http.getSharedObject(ApplicationContext.class);
   // 创建CorsFilter
   CorsFilter corsFilter = getCorsFilter(context);
   Assert.state(corsFilter != null, () -> "Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
         + CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean.");
   http.addFilter(corsFilter);
}
  • • getCorsFilter(...):
  • • 容器中若是有CorsFilter直接返回
  • • 容器中若是有CorsConfigurationSource,那就为其包装为CorsFilter返回
private CorsFilter getCorsFilter(ApplicationContext context) {
   if (this.configurationSource != null) {
      return new CorsFilter(this.configurationSource);
   }
   //从容器中获得CorsFilter过滤器
   boolean containsCorsFilter = context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
   if (containsCorsFilter) {
      return context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);
   }
   //获取Cors匹配容器
   boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
   if (containsCorsSource) {
      CorsConfigurationSource configurationSource = context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME,
            CorsConfigurationSource.class);
      return new CorsFilter(configurationSource);
   }
   boolean mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, context.getClassLoader());
   if (mvcPresent) {
      return MvcCorsFilter.getMvcCorsFilter(context);
   }
   return null;
}

1.2 CorsConfiguration

  • • CorsConfigurationSource是根据请求获取CorsConfiguration,也就Cors规则
  • • 简单的看下里面的规则
/**
     * Cors配置默认的允许的请求方式
     */
    private static final List<String> DEFAULT_PERMIT_METHODS = Collections.unmodifiableList(
          Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name()));

    /**
     * 默认允许全部
     */
    private static final List<String> DEFAULT_PERMIT_ALL = Collections.singletonList(ALL);

    /**
     * 允许的来源
     */
    @Nullable
    private List<String> allowedOrigins;

    /**
     * 允许的请求方式
     */
    @Nullable
    private List<String> allowedMethods;

    /**
     * 允许的请求方式
     */
    @Nullable
    private List<HttpMethod> resolvedMethods = DEFAULT_METHODS;

    /**
     * 跨域请求允许携带的请求头
     */
    @Nullable
    private List<String> allowedHeaders;

    /**
     * 不懂
     */
    @Nullable
    private List<String> exposedHeaders;

    /**
     * 应该是客户端是否允许发送Cookie
     */
    @Nullable
    private Boolean allowCredentials;

    /**
     * 预检查请求的有效期
     */
    @Nullable
    private Long maxAge;

2. CorsFilter

  • • 作为SpringSecurity处理Cors的过滤器,其源码很少
  • • 就是从CorsConfigurationSource中获取CorsConfiguration,然后把CorsProcessor丢进CorsProcessor判断是否跨域请求以及对跨域请求的处理
public class CorsFilter extends OncePerRequestFilter {
   private final CorsConfigurationSource configSource;

   private CorsProcessor processor = new DefaultCorsProcessor();
   ...
   @Override
   protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
         FilterChain filterChain) throws ServletException, IOException {

      CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
      boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
      if (!isValid || CorsUtils.isPreFlightRequest(request)) {
         return;
      }
      filterChain.doFilter(request, response);
   }
   ...
}

2.1 DefaultCorsProcessor

  • • CorsProcessor是Spring-Web包下的,其实现也只有一个DefaultCorsProcessor
  • • 我们看下其入口方法,主要就是非跨域请求直接放行,不是的话就调用handleInternal(...)方法
@Override
    @SuppressWarnings("resource")
    public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
          HttpServletResponse response) throws IOException {

       //添加一些响应头
       Collection<String> varyHeaders = response.getHeaders(HttpHeaders.VARY);
       if (!varyHeaders.contains(HttpHeaders.ORIGIN)) {
          response.addHeader(HttpHeaders.VARY, HttpHeaders.ORIGIN);
       }
       if (!varyHeaders.contains(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD)) {
          response.addHeader(HttpHeaders.VARY, HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
       }
       if (!varyHeaders.contains(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS)) {
          response.addHeader(HttpHeaders.VARY, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
       }

       // 不是跨域请求直接放行
       if (!CorsUtils.isCorsRequest(request)) {
          return true;
       }

       if (response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) != null) {
          logger.trace("Skip: response already contains "Access-Control-Allow-Origin"");
          return true;
       }

       // 是否是跨域请求
       boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
       // 如果是预检查请求,但是又没有Cors数据源,就直接返回false
       if (config == null) {
          if (preFlightRequest) {
             rejectRequest(new ServletServerHttpResponse(response));
             return false;
          }
          else {
             return true;
          }
       }

       //  如果是预检查请求检查是否支持,其他类型的直接返回True
       return handleInternal(new ServletServerHttpRequest(request), new ServletServerHttpResponse(response), config, preFlightRequest);
    }
  • • 我们再看下handleInternal(...)方法:如果是预检查(跨域)请求检查是否支持,其他类型的直接返回True
  • • 本质上就是在满足跨域规则的情况下,添加对应的响应头
protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
          CorsConfiguration config, boolean preFlightRequest) throws IOException {

       String requestOrigin = request.getHeaders().getOrigin();

       // 检查来源
       // 获得请求来源
       String allowOrigin = checkOrigin(config, requestOrigin);
       if (allowOrigin == null) {
          logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
          rejectRequest(response);
          return false;
       }

       HttpHeaders responseHeaders = response.getHeaders();

       // 检查请求方式
       // 拿到此次预检查请求代表的真正请求的请求方式
       HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
       List<HttpMethod> allowMethods = checkMethods(config, requestMethod);
       // 检查请求方式是否允许,为空就代表不支持
       if (allowMethods == null) {
          logger.debug("Reject: HTTP '" + requestMethod + "' is not allowed");
          rejectRequest(response);
          return false;
       }

       // 检查请求头
       // 拿到预检查请求对应的的真正请求会携带的请求头
       List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
       // 检查请求头是否允许,为空就代表不支持
       List<String> allowHeaders = checkHeaders(config, requestHeaders);
       if (preFlightRequest && allowHeaders == null) {
          logger.debug("Reject: headers '" + requestHeaders + "' are not allowed");
          rejectRequest(response);
          return false;
       }

       // 告诉客户端允许来自这的请求
       responseHeaders.setAccessControlAllowOrigin(allowOrigin);

       // 是预检查请求, 告诉客户端允许这种请求方式
       if (preFlightRequest) {
          responseHeaders.setAccessControlAllowMethods(allowMethods);
       }

       if (preFlightRequest && !allowHeaders.isEmpty()) {
          responseHeaders.setAccessControlAllowHeaders(allowHeaders);
       }

       if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
          responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
       }

       // 估计是允许Cookie
       if (Boolean.TRUE.equals(config.getAllowCredentials())) {
          responseHeaders.setAccessControlAllowCredentials(true);
       }

       // 是预检查请求, 设置预检查请求有效期
       if (preFlightRequest && config.getMaxAge() != null) {
          responseHeaders.setAccessControlMaxAge(config.getMaxAge());
       }

       response.flush();
       return true;
    }


标签:SpringSecurity5.6,return,请求,request,CorsFilter,源码,response,跨域
From: https://blog.51cto.com/u_14008019/7514203

相关文章

  • 人人都能学的数据分析体系课(16周完整版+源码+PDF课件)
    点击下载——人人都能学的数据分析体系课(16周完整版+源码+PDF课件)  提取码:nsep 人人都能学的数据分析体系课(16周完整版+源码+PDF课件),数据也称为观测值,是实验、测量、观察、调查等的结果。数据分析中所处理的数据分为定性数据和定量数据。只能归入某一类而不能用数值进行测度的数......
  • 程序开发:在线报名线下活动小程序源码功能解析
    针对线下活动在线报名场景的小程序,支持在线支付费用以及线下核销。可以应多大多数的线下报名,线上报名客服表单可以定制订单,支持导出报名,支持审核,支持分享,支持分销拓客以及线下核销。WEB+小程序双端管理活动,日常使用更方便,可以随时随地便捷的对活动进行设置管理,支持活动发布、查看收......
  • Django框架高级之DRF部分源码分析
    【一】DRF之请求执行流程和APIView源码分析【二】DRF之Request源码分析【三】DRF之Response源码分析【四】DRF之登录认证源码分析......
  • 世界第5大搜索引擎Yandex爆出源码后获得的其内部若干排名因素
    相关新闻:中文翻译版:https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9394501005789721090%22%7D&n_type=-1&p_from=-1英文版:https://www.hackread.com/yandex-source-code-hacked-leaked/  =============================      ========......
  • DRF之Response源码分析
    【一】响应类的对象Response源码【1】路由fromdjango.contribimportadminfromdjango.urlsimportpathfrombookimportviewsurlpatterns=[path('admin/',admin.site.urls),path('test/',views.TestView.as_view()),]【2】视图fromrest_frame......
  • 小程序AI换脸开发源码
      大家最近也听说关于换脸的新闻了,不过换脸的功能还是以娱乐和文化体验为主的。其中AI换脸也是需要数据接口的对接,AI换脸技术被广泛应用于各个领域,尤其是小程序开发。今天我们就来解析一下小程序AI换脸开发源码的主要功能。  一、AI模型训练与优化  AI换脸技术是以平......
  • 人人都能学的数据分析体系课(16周完整版+源码+PDF课件)
    点击下载——人人都能学的数据分析体系课(16周完整版+源码+PDF课件) 提取码:4vxi人人都能学的数据分析(16周完整版+源码+PDF课件),其中包含Excel从入门到表格分析、从0开始学SQL、数据可视化利器Tableau、Python实现数据分析、Python实现网络爬虫、构建用户画像、预售销售额、调整运......
  • springboot vue电子班牌系统源码,以云平台、云服务器为基础,融合课程管理、物联控制、
    随着时代进步,数字信息化不断发展,很多学校都开始了数字化的转变。智慧校园电子班牌系统是电子班牌集合信息化技术、物联网、智能化,电子班牌以云平台、云服务器为基础,融合了班级文化展示、课程管理、物联控制、教务管理、考勤管理、素质评价、资源管理、家校互联等一系列应用。实现了......
  • 直播源码,zabbix忘记登录密码
    直播源码,zabbix忘记登录密码 1.首先登陆到数据库,选择zabbix数据库,查看Admin用户。 mysql>usezabbixReadingtableinformationforcompletionoftableandcolumnnamesYoucanturnoffthisfeaturetogetaquickerstartupwith-ADatabasechangedmysql>select......
  • 视频直播系统源码,Android EditText不显示提示文字hint
    视频直播系统源码,AndroidEditText不显示提示文字hintEditText不显示提示文字hint原因,有可能是hint的字体颜色和EditText的背景颜色一样,需要设置颜色值android:textColorHint="@color/gray"。<EditText      android:id="@+id/ed_name"      android:la......