博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
为什么出现OPTIONS?SpringBoot接口跨域解决方案
阅读量:6721 次
发布时间:2019-06-25

本文共 3938 字,大约阅读时间需要 13 分钟。

原文地址:

引言

前后端分离的项目虽然降低了耦合度,但是引发的各种问题也随之而来。后端项目由Tomcat部署(监听8080端口),前端项目则部署在Nginx上(监听80、443等非8080端口),前端页面加载速度大大提高了,而当ajax请求后端接口的时候却报错了。

同源策略

同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。

前端地址: http://127.0.0.1:63344

后端地址: http://127.0.0.1:8080

这两个地址虽然ip地址和协议都一样,但是端口不一样,所以并不满足同源,这样就造成了跨域的问题。

解决方案

配置addCorsMappings

新增一个类实现 WebMvcConfigurer 接口,然后给这个类加上 @Configuration 注解,最后实现 addCorsMappings 方法就ok了。

@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {	@Override    public void addCorsMappings(CorsRegistry registry) {        registry.addMapping("/**")                .allowedOrigins("*")                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")                .maxAge(3600)                .allowCredentials(true);    }}复制代码

请求正常

这就完事了?这么简单的吗?

登陆后台管理看看

wtf?

为何是OPTIONS请求?

按照我的逻辑,上面那个报错的接口应该是一个GET请求,可为什么发的是OPTIONS请求?翻阅各种资料,各种百度google,各种倒腾,各种头疼,最后。。。我掀开了被子,惊奇的发现。。。被窝可真暖和啊!(先睡一觉再说)

原因

原来浏览器会在发送真正请求之前,先发送一个方法为OPTIONS的预检请求 Preflighted requests 这个请求是用来验证本次请求是否安全的,而且并不是所有请求都会发送,需要符合以下条件:

  • 请求方法不是GET/HEAD/POST
  • POST请求的Content-Type并非application/x-www-form-urlencoded, multipart/form-data, 或text/plain
  • 请求设置了自定义的header字段

对于管理端的接口,我有对接口进行权限校验,每次请求需要在header中携带自定义的字段(token),所以浏览器会多发送一个OPTIONS请求。

那为什么OPTIONS请求报错了。。。

经过debug发现,OPTIONS请求只会携带自定义的字段,并不会将相应的值带入进去,而后台校验token字段时 token为NULL,所以验证不通过,抛出了一个异常。

放行OPTIONS

我换了一种解决跨域的方法,使用拦截器解决跨域问题,并且针对OPTIONS请求做放行处理。

新增一个拦截器类 CorsInterceptor 实现 HandlerInterceptor 接口

@Componentpublic class CorsInterceptor implements HandlerInterceptor {    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {        response.setHeader("Access-Control-Allow-Origin", "*");        response.setHeader("Access-Control-Allow-Credentials", "true");        response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");        response.setHeader("Access-Control-Max-Age", "86400");        response.setHeader("Access-Control-Allow-Headers", "*");        // 如果是OPTIONS则结束请求        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {            response.setStatus(HttpStatus.NO_CONTENT.value());            return false;        }        return true;    }}复制代码

需将跨域拦截器放在校验拦截器之上

我把原来的跨域配置 addCorsMappings 删除了

@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {    @Resource    private LoginInterceptor loginInterceptor;    @Resource    private CorsInterceptor corsInterceptor;    @Override    public void addInterceptors(InterceptorRegistry registry) {		// 跨域拦截器需放在最上面        registry.addInterceptor(corsInterceptor).addPathPatterns("/**");		// 校验token的拦截器        registry.addInterceptor(loginInterceptor).addPathPatterns("/admin/**");    }}复制代码

OPTIOINS请求没问题了

也相继发送了GET请求

总结

晚安!

补充说明

Access-Control-Allow-Credentials 响应头表示是否可以将对请求的响应暴露给页面。返回 true 则可以,其他值均不可以。Credentials可以是 cookies, authorization headersTLS client certificates。如果被设置为true,服务器不得设置 Access-Control-Allow-Origin 的值为 *,需要指定域名,否则当需要发送 Cookie 到服务器时,浏览器响应时将会报错。

测试

Ajax请求开启 withCredentials 属性

xhrFields: {	withCredentials: true}复制代码

后端跨域配置不变时,浏览器请求结果报错

指定 Access-Control-Allow-Origin 的值为 http://127.0.0.1:63344,再次请求后正常

最终配置

@Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {		// 此处配置的是允许任意域名跨域请求,可根据需求指定        response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));        response.setHeader("Access-Control-Allow-Credentials", "true");        response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");        response.setHeader("Access-Control-Max-Age", "86400");        response.setHeader("Access-Control-Allow-Headers", "*");        // 如果是OPTIONS则结束请求        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {            response.setStatus(HttpStatus.NO_CONTENT.value());            return false;        }        return true;    }复制代码

参考

转载地址:http://ubnmo.baihongyu.com/

你可能感兴趣的文章
Mac配置NDK和SDK
查看>>
Android SDK 下载提速
查看>>
类与结构体
查看>>
linux十三周五次课(5月8日)笔记
查看>>
ssh免密码登录设置
查看>>
层次分析法(AHP)详解
查看>>
解决西安不能登录aifang及aifang后台的处理过程
查看>>
centos tar压缩与解压缩命令大全
查看>>
一个Web报表项目的性能分析和优化实践(五):重构有助于性能优化么?
查看>>
Windows下安装cacti
查看>>
静态路由实验
查看>>
PSR-0规范
查看>>
windows系统中查看linux分区的三种方式
查看>>
Linux下安装DBD::Oracle、DBI和Ora2Pg
查看>>
一次性统计SQL Server中全部表的数量和行的数量
查看>>
四肢瘫痪者植入芯片后首次自己吃饭
查看>>
JAVA集合
查看>>
akka进阶(1) - supervisor strategy
查看>>
iptables之mangle表应用实现策略路由+(案例)
查看>>
Linux系统基本操作
查看>>