跨域问题处理(已处理IOS兼容问题)

mac2025-04-05  9

引言

目前常用的Ajax跨域问题解决方式有三种

Jsonp方式,需要前端和后端同时处理nginx(或者其它HTTP服务器)配置后端设置CORS允许跨域

本文着重讲后端设置跨域,案例基于SpringBoot,其它框架原理相同

(一) CORS跨域

CORS是一个W3C标准,全称是"跨域资源共享",CORS有两种请求,简单请求和非简单请求。

(1) 只要同时满足以下两大条件,就属于简单请求。

请求方法是以下三种方法之一: HEADGETPOST HTTP的头信息不超出以下几种字段 AcceptAccept-LanguageContent-LanguageLase-Event-IDContent-Type: 只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

(2) 对于非简单请求,会发出一次预检测请求,返回码是204,预检测通过后,才会真正发出请求。

 

(3) 后端CORS处理方式

使用Filter过滤器,对所有的请求的response添加header 在SpringBoot中实现Filter接口,并且设置过滤器的顺序为最前@Order(-Integer.MAX_VALUE)

@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "*"); if (HttpMethod.OPTIONS.toString().equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpStatus.NO_CONTENT.value()); } else { filterChain.doFilter(request, response); } }

此处常规情况跨域可以解决,但是某些情况下,在IOS系统下该配置会失效,在发起option请求得到返回204状态码之后,不再进行下一步的调用。经过分析后,发现是

由于header里面带了很多请求参数,而上面的response.setHeader("Access-Control-Allow-Headers", "*");允许所有头部没有生效导致。 但是在PC浏览器或者安卓手机上,该方式又能生效(烦人的IOS兼容问题,找了好久才找到)。

找到问题后,开始对上面的代码进行了改造,由于项目中不同地方的接口可能header中的某些参数不一致,所以决定采用前端传了什么header,就在response中设置允许什么header,改造后的代码如下。

@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Max-Age", "3600"); /**cors modified start**/ StringBuilder headers = new StringBuilder(); Enumeration<String> headerNames = request.getHeaders("Access-Control-Request-Headers"); if(Objects.nonNull(headerNames)) { while (headerNames.hasMoreElements()) { headers.append(headerNames.nextElement()).append(","); } } response.setHeader("Access-Control-Allow-Headers", headers.toString()); /**cors modified end**/ if (HttpMethod.OPTIONS.toString().equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpStatus.NO_CONTENT.value()); } else { filterChain.doFilter(request, response); } }

(二) Jsonp跨域方式

jsonp的跨域实现原理是在HTML标签里,一些标签比如script、img这样的获取资源的标签是没有跨域限制的,src或href链接的静态资源,本质上来说也是一个get请求。

jsonp就是利用这一策略,从服务端拿到信息之后,利用回调callbak回调页面上的方法(前后端约定),进行渲染,所有jsonp只能处理get请求。 

具体本文不做详细介绍,具体可查看Jsonp跨域原理解析

(三) Nginx配置跨域

nginx配置跨域原理和后端实现类似,也是通过设置header 里面参数实现。

location / { if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true always; add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS' always; add_header Access-Control-Allow-Headers 'Authorization,X-Requested-With,Content-Type,Origin,Accept' always; add_header Access-Control-Max-Age 3600; add_header Content-Length 0; return 200; } add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true always; add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS' always; add_header Access-Control-Allow-Headers 'Authorization,X-Requested-With,Content-Type,Origin,Accept' always; proxy_pass http://localhost:8081/; }

具体可以查看nginx方向代理解决跨域

(四) 冷门跨域问题

(1) canvas操作图片的跨域问题。

以前做微信公众号生成海报时,就遇到过这问题。

主要原理是利用HTML的crossOrigin属性,通过设置crossOrigin=anonymous。具体参考解决canvas图片getImageData,toDataURL跨域问题

最新回复(0)