之前写了篇关于 JSONP 和 CORS 解决跨域请求的博客,在最近和深圳凹凸团队前后端联调时实打实的实战了一把 CORS。还是应了纸上得来终觉浅的老话,因为实际运用中会存在不同的状况,只是看文档理解
顺晟科技
2021-06-16 10:58:22
270
之前写过一篇关于JSONP和CORS解决跨域请求的博客,最近和深圳凸凹团队前后联调的时候和CORS打了一场真仗。还是应该回答那句老话,纸上得到的东西总是很容易感觉到的,因为实际应用中会有不同的情况,仅仅看文档理解概念并不能真正成为实战派。
在这次联合调试中,前端和后端是分开的,数据接口由后端的Spring MVC提供,数据渲染在前端以异步方式完成。与以往不同的是,由于前端开发全部交给深圳凹实验室,静态文件全部运行在一个独立的域名上,这就是JD.COM的田童大厦项目。因此,来自前端的所有请求都变成了跨域请求。
JSONP是解决跨域请求被浏览器拒绝的聪明绝招,但不能解决POST跨域的问题。联合调试的界面是跨域上传头像,POST发送FormData对象。因此,服务器CORS处理它。
在服务器端,Spring MVC设置CORS非常简单。如果springframework版本是4.2或更高版本,Spring MVC可以通过注释@CrossOrigin直接为标记的控制器方法设置CORS。例如,以下示例代码:
@ cross origin(origin=' http://localhost :9000 ')
@ GetMapping('/问候语')
公共问候语(@RequestParam(必选=false,默认值='World ')字符串名称){
System.out.println('====在问候语===');
返回新的问候语(counter.incrementAndGet(),String.format(template,name));
}
@CrossOrigin注释可以确定跨域源域、请求类型、请求头等。CORS通过设置诸如原点、方法、更大值、允许值、允许值等参数来接受。如果源设置为星号,则所有源域都允许跨域请求,如果方法设置为开机自检,则只允许请求类型为开机自检的跨域请求。
前端正常发送异步请求,类似于下面的代码:
var formData=new formData();
formData.append('avatar ',' data : image/png;base64,ivborw 0 ggoaaaaansuhugaaaaaaaaaaaaaabaqmaaal 21 bkaaaaaaaaaa1bmvex/TQBcNTh/aaaaaaaaxrstlmp 0 jrw/qaaaapjrefejygaaayaaz 3 fkgaaaaaaaaasuvor k5cyii=');
var req=new XMlhttprequest();
req.open('POST ','//XXX . JD.com/XXX/XXX ');
req . send(FrOm DATa);
然而,结果并不令人满意。返回代码302。请求被强制跳转到JD.COM的统一登录。这是个问题。因为上传头像需要登录状态,所以浏览器在请求时一定要带本地cookies。如果是JSONP,默认会传递cookies。因此,需要将XMLHttpRequest的凭证信息标识位设置为真:
var req=new XMlhttprequest();
req.withCredentials=true
这里需要注意的一点是,如果请求带有cookies,服务器上的CORS的来源不能是*,并且必须明确指定允许的来源。再次联合调试测试后,跨域失败。查看服务器日志,发现请求根本没有进入控制器层,在登录拦截器中被拒绝。这是另一个问题,因为在Spring MVC中设置了拦截器,所有符合规则的请求都会被拦截器拦截,或者跨域的请求会被排除,但这是不可取的,因为必须由登录的用户来完成。因此,需要在拦截器的preHandle()方法中进行处理,将源字段添加到响应头的访问控制允许源字段中,并设置访问控制允许头:
公共类LoginInterceptor扩展HandlerInterceptorAdapter{
//在实际处理程序执行之前
public boolean preHandle(HttpServletrequest请求,
HttpServletResponse响应,对象处理程序)引发异常{
response . addheader(' Access-Control-Allow-Origin ',' http://H5 . m . JD.com ');
response . addheader(' Access-Control-Allow-Methods ',' GET,PUT,POST,DELETE,OPTIONS ');
response . setheader(' Access-Control-Allow-Headers ',' Content-Type,Authorization,X-Requested-With,isAjaxRequest ');
返回true
}
}
这次以为还可以,还是没打通,就摔了!但是这次有进展,因为请求进入了Controller的方法,头像上传完毕,但是浏览器的响应体是空的。打开浏览器的控制台,看到一条错误消息:
XMLHttpRequest无法加载http://xxxp.jd.com/xxx/xxx.凭据标志为“真”,但“访问控制-允许-凭据”标头为“”。它必须是“真”才能允许凭据。因此,不允许访问Origin 'http://h5.m.jd.com '。
这是第三个问题。开机自检请求标有凭证信息标识位,但服务器返回的响应头的访问控制允许凭证字段为空,不是真。解决办法显而易见。只需将HttpServletResponse字段设置为true:
public boolean preHandle(HttpServletrequest请求,
HttpServletResponse响应,对象处理程序)引发异常{
response . addheader(' Access-Control-Allow-Credentials ',' true ');
返回true
}
在这个处理之后,浏览器接收由控制器层方法返回的JSON结果。
以上是CORS在服务器端实现跨域请求的调试过程。其实从结果来看,真的很简单,但是如果实际开发中没有遇到一些问题,很容易被忽视,光看文档怎么用是不够的。
# HTTP