米扑博客米扑代理,最近加了腾讯云的CDN(内容分发网络),将网站的静态文件(js、css、txt、img、fonts)上传到腾讯云的对象存储里,然后配置CDN,绑定CDN域名,例如:

米扑博客:https://blog.mimvp.com  +  https://cdn-blog.mimvp.com

米扑代理:https://proxy.mimvp.com  +  https://cdn-proxy.mimvp.com

 

腾讯云CDN的静态文件(js、css、txt、img、fonts)都能访问(如果不能访问,请检查是否是黑白名单或防盗链的问题),但是网站的图标(font awesome)不能显示,打开浏览器控制台报错如下

has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

@font-face  iconfont 如下:

@font-face {
    font-family: "iconfont";
    src: url('/fonts/iconfont.eot?t=1575438202903');
    src: url('/fonts/iconfont.eot?t=1575438202903#iefix') format('embedded-opentype'),
	url('/fonts/iconfont.woff?t=1575438202903') format('woff'),
	url('/fonts/iconfont.ttf?t=1575438202903') format('truetype'),
	url('/fonts/iconfont.svg?t=1575438202903#iconfont') format('svg')
}

具体报错如下:

A cookie associated with a cross-site resource at <URL> was set without the `SameSite` attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at <URL> and <URL>.
11:40:43.972 (index):1 Access to font at 'https://cdn-proxy.mimvp.com/fonts/iconfont.woff?t=1575438202903' from origin 'https://proxy.mimvp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
11:40:43.984 (index):400 GET https://cdn-proxy.mimvp.com/fonts/iconfont.woff?t=1575438202903 net::ERR_FAILED
11:40:44.148 (index):1 Access to font at 'https://cdn-proxy.mimvp.com/fonts/iconfont.ttf?t=1575438202903' from origin 'https://proxy.mimvp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
11:40:44.150 (index):1 GET https://cdn-proxy.mimvp.com/fonts/iconfont.ttf?t=1575438202903 net::ERR_FAILED

具体报错截图:

 

解决办法:

登录 腾讯云控制台 —> 内容分发网络 —> 域名管理 —> 高级配置 —> HTTP Header配置

 

添加 HTTP Header

Access-Control-Allow-Methods	:	GET, POST, OPTIONS, PUT, DELETE
Access-Control-Expose-Headers	:	Authorization
Access-Control-Max-Age			:	2592000
Access-Control-Allow-Origin		:	*

说明:添加一条,即可成功解决 

Access-Control-Allow-Origin : *   // * 表示允许所有类型的请求,一般推荐具体的 URL 网址

Access-Control-Expose-Headers : Authorization  // 允许在请求头里存放token,后端通过请求头来获取前端传来的token

 

补充

腾讯云CDN 添加 HTTP Header 的完整参数如下:

 

添加 header 以后的 Response Headers

注:正式线上,推荐 Access-Control-Allow-Origin : https://proxy.mimvp.com  使用具体域名,不用通配符 *

ResponseHeaders
access-control-allow-methods: GET, POST, OPTIONS, PUT, DELETE
access-control-allow-origin: https://proxy.mimvp.com
access-control-expose-headers: Authorization
access-control-max-age: 2952000
cache-control: max-age=600
content-length: 1148
content-type: application/font-woff

 

 

CORS(Cross-Origin Resource Sharing)跨域

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跨越问题造成的,解决办法无非有两种方式:响应头(headers)添加参数 添加过滤器

下面就详细说说CORS跨越问题的起因与详细解决办法。

 

2、CORS(Cross-Origin Resource Sharing)

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 请求所带来的风险。

 

什么情况下存在跨域问题

1)本文提到的由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求

2)Web 字体 (CSS 中通过 @font-face 使用跨域字体资源),网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用

@font-face {
    font-family: "iconfont";
    src: url('/fonts/iconfont.eot?t=1575438202903');
    src: url('/fonts/iconfont.eot?t=1575438202903#iefix') format('embedded-opentype'),
	url('/fonts/iconfont.woff?t=1575438202903') format('woff'),
	url('/fonts/iconfont.ttf?t=1575438202903') format('truetype'),
	url('/fonts/iconfont.svg?t=1575438202903#iconfont') format('svg')
}

3)WebGL 贴图

4)使用 drawImage 将 Images/video 画面绘制到 canvas

5)样式表(使用 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 :*

或者

Access-Control-Allow-Origin :https://proxy.mimvp.com

 

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() {
        ……
    }
}

个人比较推荐使用上述的三种方式之一,推荐请求头 headers 设置。

 

 

问题:

在某域名(blog.mimvp.com)下使用Ajax向另一个域名(proxy.mimvp.com)下的页面请求数据,会遇到跨域问题。

另一个域名(proxy.mimvp.com)必须在response中添加Access-Control-Allow-Origin的header,才能让前者(blog.mimvp.com)成功拿到数据。

 

怎样才能算跨域?

协议,域名,端口都必须相同,才算在同一个域。

例如:https  blog.mimvp.com  443  则算同一个域

 

当跨域访问时,浏览器会发请求吗?

这是真正困扰我们的问题,因为我们不清楚浏览器会怎么做。

它会不会检查到你要请求的地址不是同一个域的,直接就禁止了呢?

在jsbin上做了一个试验,使用Chrome打开。当点击“Run with Js”时,控制台“Console”上会打出:

XMLHttpRequest cannot load http://google.com/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://run.jsbin.io' is therefore not allowed access.  

但开发者工具的”Network”栏并没有任何记录,它到底发请求了没?

又使用python -m SimpleHTTPServer在本地创建了一个小服务器,然后把地址改成它,结果发现在python这边的确打印出请求来了,可见浏览器的确发出了请求。

Access-Control-Allow-Origin

现在该Access-Control-Allow-Origin出场了。只有当目标页面的response中,包含了Access-Control-Allow-Origin这个header,并且它的值里有我们自己的域名时,浏览器才允许我们拿到它页面的数据进行下一步处理。

例如:Access-Control-Allow-Origin : http://run.oicqzone.com

如果它的值设为*,则表示谁都可以用:Access-Control-Allow-Origin : *

但是,在正式产品环境中,没人会用 * ,推荐使用 URL 

例如:Access-Control-Allow-Origin : https://proxy.mimvp.com

 

 

参考推荐:

腾讯云 CDN 跨域问题:has been blocked by CORS policy

浏览器和服务器实现跨域(CORS)判定的原理

跨域(CORS)请求的几种解决方法

window.name实现的跨域数据传输

JavaScript 跨域总结与解决办法

CDN 源站与回源host 的区别

做网页很实用代码集合和CSS制作网页小技巧整理