CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)

简介: CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)
@CrossOrigin初始化


关于此注解的初始化,在完成mapping注册的时候就已经完成了,大致步骤如下:


AbstractHandlerMethodMapping:
  // 注册一个mapping
  public void registerMapping(T mapping, Object handler, Method method) {
    this.mappingRegistry.register(mapping, handler, method);
  }
  // 内部类
  class MappingRegistry {
    // 记录着没一个HandlerMethod所对应的注解配置
    private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
    ...
    public void register(T mapping, Object handler, Method method) {
      ...
      // initCorsConfiguration这里就是解析handler上面的注解喽~~~
      // 此init方法只有RequestMappingHandlerMapping子类重写了~~~
      CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
      if (corsConfig != null) { // 若不为null(有注解配置),就缓存起来
        this.corsLookup.put(handlerMethod, corsConfig);
      }
      ...
    }
  }


对于handler上次注解的解析,最终是由RequestMappingHandlerMapping完成的:


它显著的特点是:和Handler强绑定,因此在注册Mapping的时候就完成初始化工作。


综上所述可得出这三种配置方式的区别:


  1. CorsFilter方式:完全独立的Filter,和其它配置并不冲突和也无关联,最终委托给CorsProcessor来完成的
  2. addCorsMappings方式:它的配置会作用于所有的内置配置的HandlerMapping上,所以它就是global全局配置
  3. @CrossOrigin方式:它和某一个具体的handler强绑定,所以它属于局部配置。


说明:方式2和方式3可以形成互补配置,有combine的效果。


为何OPTIONS请求进入不了Controller的Handler方法内?


这个问题是系列文章的第一篇我抛出来的,因为有一个现象是:简单请求我可以在Controller的方法内向response手动添加请求头搞定。但是非简单请求这么做行不通了,原因是OPTIONS请求根本进入不了方法体~


阅读完本文的上半拉,此问题的答案就显而易见了,因此我此处不再废话。倘若对此问题还没想到答案的小伙伴,欢迎你在下面给我留言我会及时解答你的。


为何给response设置响应头写在postHandle()方法内无效?


这个问题倒是困扰了我好一会,直到我直到了Spring MVC对它的处理过程。

问题的现象是:response的响应头都有,但http状态码却是403,跨域失败。结果如下截图:


image.png


针对此问题作出如下解释供以参考:


  1. 上面有说到一句话:匹配上handler后,若是OPTIONS请求的话,它最终的handler不是原handler而是一个全新的PreFlightHandler处理器,并且并且并且chain上的拦截器们都是会生效的。
  2. 关键就在这里:PreFlightHandler执行handler处理方法最终是委托给CorsProcessor执行的,若config == null并且是 预检请求 ,那它就会执行:rejectRequest(serverResponse),这时状态码就已经设置为了403了,因此等handler方法执行完成之后再执行postHandle()方法体,因为返回状态码已经设置好,已经无力回天了,so就出现了如此怪异现象~


有人说在postHandle()方法里加上这么一句,手动把响应码改成200:response.setStatus(HttpStatus.OK.value());。

效果:能达到想要的跨域效(真实请求能继续发送)。但是我强烈不建议你这么去做,因此这样你需要加很多逻辑判断(什么时候应该设置,什么时候不应该),得不偿失。


DispatcherServlet.doOptions()方法简单分析

说明:dispatchOptionsRequest这个参数虽然默认值是false,但在DispatcherServlet所有的构造器里都有这么一句:setDispatchOptionsRequest(true)。


FrameworkServlet:
  /** Should we dispatch an HTTP OPTIONS request to {@link #doService}?. */
  private boolean dispatchOptionsRequest = false;
  @Override
  protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 若dispatchOptionsRequest = true 或者是预检请求OPTIONS请求,都会processRequest
    // processRequest(request, response);就是复杂的视图渲染逻辑~~~
    if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
      processRequest(request, response);
      // 若你自己设置了allow响应头,那就不处理了。否则交给下面处理
      if (response.containsHeader("Allow")) {
        // Proper OPTIONS response coming from a handler - we're done.
        return;
      }
    }
    // Use response wrapper in order to always add PATCH to the allowed methods
    // 开发者自己没有设置Allow这个响应头就会进这里来,最终效果是
    // Allow:GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
    super.doOptions(request, new HttpServletResponseWrapper(response) {
      @Override
      public void setHeader(String name, String value) {
        if ("Allow".equals(name)) {
          value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
        }
        super.setHeader(name, value);
      }
    });
  }


若CORS请求的URL不存在,响应码404还是403?


  • 无默认的servlet处理器(DefaultServletHandler):404(找不到对应的handler)
  • 有默认的servlet处理器:403(能找到handler,因为有默认的处理器兜底嘛)


Spring MVC的这个配置用于开启默认处理器与否:


    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        //configurer.enable();
        //configurer.enable("default");
    }


总结


@CrossOrigin关注方法,CorsFilter等其它方式更关注URL。

该CORS系列通过三篇文章层层递进的讲述了CORS跨域请求访问自己如何处理等相关议题,在前后端分离开发模式的今天我觉得后端程序员有必要掌握这块内容,因此特撰文分享给大家,我觉得应该是能对很多人有较大帮助的,所你还有别的想法,欢迎你给我留言~


相关文章
|
人工智能 前端开发 JavaScript
webpack-dev-server代理后端一直报CORS跨域或500错误
在Vue项目中使用Webpack的devServer代理后端接口时,遇到500错误。问题根源在于浏览器请求中携带的Origin头导致服务器报错,而Postman测试正常。通过分析发现,调整或移除Origin头可解决问题。解决办法包括:1) 在代理配置中添加正确的Origin头;2) 删除请求中的Origin头。文章还深入解析了Origin头的作用及changeOrigin配置的实际意义,并附带相关文档链接,帮助开发者更好地理解与解决类似跨域问题。
865 20
|
8月前
|
安全 API PHP
PHP中实现CORS跨域资源共享的方法
通过这种方式,你可以在PHP应用中灵活地实现CORS,以支持跨域Web应用的需求。
560 15
|
前端开发 JavaScript 应用服务中间件
前端跨域问题解决Access to XMLHttpRequest at xxx from has been blocked by CORS policy
跨域问题是前端开发中常见且棘手的问题,但通过理解CORS的工作原理并应用合适的解决方案,如服务器设置CORS头、使用JSONP、代理服务器、Nginx配置和浏览器插件,可以有效地解决这些问题。选择合适的方法可以确保应用的安全性和稳定性,并提升用户体验。
9219 90
|
JSON 缓存 前端开发
对CORS(跨域)的一些见解
CORS(跨域资源共享)是W3C标准,用于解决AJAX跨源请求限制。浏览器与服务器需共同支持CORS,浏览器自动处理请求头,开发者无需额外操作。CORS分为简单请求与非简单请求:简单请求满足特定条件(如方法为GET/POST/HEAD且头信息有限制),浏览器直接发送;非简单请求需先进行“预检”请求(OPTIONS方法),确认服务器允许后才发送实际请求。服务器回应需包含Access-Control-Allow-Origin等字段,以控制跨域访问权限。
369 10
|
设计模式 前端开发 Java
了解 Spring MVC 架构、Dispatcher Servlet 和 JSP 文件的关键作用
Spring MVC 是 Spring 框架的一部分,是一个 Web 应用程序框架。它旨在使用 Model-View-Controller(MVC) 设计模式轻松构建Web应用程序。
323 0
|
前端开发 Java 程序员
38SpringMVC - SpringMVC架构
38SpringMVC - SpringMVC架构
165 0
|
存储 设计模式 前端开发
什么是SpringMVC?简单好理解!什么是应用分层?SpringMVC与应用分层的关系? 什么是三层架构?SpringMVC与三层架构的关系?
文章解释了SpringMVC的概念和各部分功能,探讨了应用分层的原因和具体实施的三层架构,以及SpringMVC与三层架构之间的关系和联系。
1118 1
什么是SpringMVC?简单好理解!什么是应用分层?SpringMVC与应用分层的关系? 什么是三层架构?SpringMVC与三层架构的关系?
|
XML JSON 前端开发
SpringMVC的架构有什么优势?(下)
SpringMVC的架构有什么优势?
|
安全 前端开发 Java
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
362 1
|
设计模式 前端开发 Java
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
665 1