本文正在参加「Java主题月 – Java Debug笔记活动」,详情查看<活动链接>
1、前言
最近在项目中,调用 Eureka
REST
接口时,出现了CORS
跨越问题(Cross-origin resource sharing
),在此与大家进行分享,避免多走些弯路。
项目前端(http://localhost:9000
)通过 Ajax
方式调用 Eureka
REST
接口(http://localhost:8761/eureka/apps
)时,却没有任何反应,则通过 F12
查看日志发现出现“Access-Control-Allow-Origin”
类似异常,详细如下:
……
http://localhost:8761/eureka/apps. Origin http://localhost:9000 is not allowed by Access-Control-Allow-Origin
……
复制代码
通过 google
,发现是由于 CORS
跨越问题造成的,解决办法无非有两种方式:响应头添加参数和添加过滤器,下面就详细说说 CORS
跨越问题的起因与详细解决办法。
2、CORS
CORS
,常被大家称之为跨越问题,准确的叫法是跨域资源共享(CORS
,Cross-origin resource sharing
),是 W3C
标准,是一种机制,它使用额外的 HTTP
头来告诉浏览器 让运行在一个 origin
(domain
) 上的 Web
应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP
请求。
http://localhost:9000
请求 http://localhost:8761/eureka/apps
就是违背了上述原则,即:请求服务器不同端口的另一个资源,出于安全原因,浏览器限制发起的跨源 HTTP
请求,则会出现本文开头提到的现象及异常。
例如,XMLHttpRequest
和 Fetch
API
遵循同源策略, 这意味着使用这些 API
的 Web
应用程序只能从加载应用程序的同一个域请求 HTTP
资源,除非使用 CORS
头。
跨域资源共享( CORS )机制允许 Web
应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API
容器中(例如 XMLHttpRequest
或 Fetch
)使用 CORS
,以降低跨域 HTTP
请求所带来的风险。
什么情况下存在跨域问题
- 本文提到的由
XMLHttpRequest
或Fetch
发起的跨域HTTP
请求。 Web
字体 (CSS
中通过@font-face
使用跨域字体资源),因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。WebGL
贴图。- 使用
drawImage
将Images/video
画面绘制到canvas
样式表(使用CSSOM
)。
面对CORS的限制,将如何解决呢
世间万物完事,有因必有果,有果必有因。当然CORS的限制,官方也是给出了解决办法的。
CORS
标准新增了一组 HTTP
头字段(Access-Control-Allow-Origin
),允许服务器声明哪些源通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP
请求方法(特别是 GET
以外的 HTTP
请求,或者搭配某些 MIME
类型的 POST
请求),浏览器必须首先使用 OPTIONS
方法发起一个预检请求(preflight request
),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP
请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies
和 HTTP
认证相关数据)。
CORS
请求失败会产生错误,但是为了安全,在 JavaScript
代码层面是无法获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。
如果有兴趣了解该机制剖析的可以参考: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
3、解决办法
在查阅大量资源,并了解过CORS机制后,解决办法实质必定会围绕 Access-Control-Allow-Origin
头。
解决办法如下:
方法1:添加响应头
在被请求资源中添加响应头信息 "Access-Control-Allow-Origin:*
方法2:过滤器
在本项目中添加如下过滤器:
/**
* 解决跨域问题
*/
public class AccessControlAllowOriginFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, response);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}
复制代码
方法3:注解方式
在 Spring Boot
中拥有大量的注解,针对跨域问题,也提供了对应的注解 @CrossOrigin
,使用方法如下:
import java.util.HashMap;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xcbeyond
*/
@RestController
@RequestMapping(value = "/api", method = RequestMethod.POST)
public class DemoController {
@CrossOrigin(origins = "*")
@RequestMapping(value = "/get")
public String get() {
……
}
}
复制代码
个人比较推荐使用上述的三种方式之一,其他方式请自己百度、谷歌吧