建站记录 | 解决腾讯云COS图片访问失败的问题
在配置hao主题时,发现【文章】设置中的【动态主色】没有生效。
进入开发者模式,查看报错信息如下:
from origin 'http://***.***' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
查询相关资料,发现是浏览器的跨域访问限制造成的。所以,首先要弄明白什么是跨域限制。
CORS跨域资源共享
同源策略
要了解什么是跨域访问,先要了解什么是“同源”。同源策略(Same Origin Policy)是浏览器的一种基本安全策略,对不同源的文档、脚本的访问做了限制。同源策略判断同源的依据是:主机相同(域名相同)、端口相同、协议相同。也就是说,除了同域名不同路径的访问,都会被浏览器视作非同源。
同源策略只在浏览器端生效,可以有效地防护跨站伪造请求(CSRF)攻击。
CSRF攻击利用受害者浏览器中的认证凭据(如Cookie、Session等),向受信任的网站发送非预期的HTTP请求。由于这些请求附带了受害者的认证信息,因此,Web服务器可能会误认为这些请求是合法用户的行为,从而执行相应的操作,如转账、修改密码等。
//来自 zhouzhou的奇妙编程
跨源资源共享
W3C推荐使用跨源资源共享(Cross-origin Resource Sharing)来处理跨域资源请求。使用CORS时,异步请求会被分解为简单请求和非简单请求:
简单请求
请求方法是:HEAD、GET、POST
HTTP头信息是:Accept、Accept-Language、Content-Language
Content-type是:application/x-www-from-urlencoded、multipart/from-data、text/plain
对于简单请求,浏览器直接发出CORS请求,在头信息中添加一个Origin字段,记录请求来源(协议+域名+端口)。服务器根据来源检查是否是允许的源。若是:返回一个HTTP回应,并加上Access-Control-Allow-Origin等字段、该资源能被浏览器正确取得。若不是:返回一个普通HTTP回应,由于缺少Access-Control-Allow-Origin字段,会抛出错误。
非简单请求
请求方法是:PUT、DELETE等
Content-type是:application/json等
对于复杂请求,浏览器在发出CORS请求前,浏览器会要求服务器检查当前域名是否在许可名单内、是否被允许使用当前字段。后续操作与简单请求类似。
使用CORS
服务端(COS服务)配置
接下来着手配置CORS跨域资源共享的访问,首先要解决服务器的认证问题。打开腾讯云COS控制台,进入使用的存储桶,在其【安全管理】-【跨域访问CORS设置】中添加一条新的访问规则:
其中,需要填写的字段(带*为必填):
来源 Origin:允许跨域请求的来源,添加自己所使用的域名、 IP 地址。
操作 Methods: GET、PUT、POST、DELETE、HEAD中的一个或多个。
Allow-Headers:在发送 OPTIONS 请求时告知服务端,接下来的请求可以使用哪些自定义的 HTTP 请求头部,可以同时指定多个 Headers,每行只能填写一个。Header 容易遗漏,没有特殊需求的情况下,建议设置为
*
,表示允许所有。在Access-Control-Request-Headers
中指定的每个 Header,都必须在 Allowed-Header 中有对应项。Expose-Headers:Expose-Header 里返回的是 COS 的常用 Header,详情请参见 公共请求头部。不允许使用通配符,大小写不敏感,支持多行且每行只能填写一个。
超时 Max-Age:设置 OPTIONS 请求得到结果的有效期(秒)。
返回 Vary: Origin:如果浏览器同时存在 CORS 和非 CORS 请求,请启用该选项,否则会出现跨域问题。
这里特别要注意Expose-Headers的设置,如果允许的公共请求头不对应、缺少,都会导致后续无法访问。
客户端(Halo服务器)配置
由于我们这次只解决文章页获取文章封面的问题,所以只需要修改文章页的模板即可。打开halo/themes/theme-hao/template/
目录下的posts.html
,找到:
<div class="coverdiv loaded" id="coverdiv">
<img alt="cover" class="nolazyload" id="post-cover"
th:src="${#strings.isEmpty(post.spec.cover) ? theme.config.layout.postRandomImg : post.spec.cover}">
</div>
在<img>
标签内新增一个属性,crossorigin="anonymous"
,结果如下:
<div class="coverdiv loaded" id="coverdiv">
<img alt="cover" class="nolazyload" id="post-cover" crossorigin="anonymous"
th:src="${#strings.isEmpty(post.spec.cover) ? theme.config.layout.postRandomImg : post.spec.cover}">
</div>
这样,双端配置就都已经完成,别忘了重新启动halo服务来刷新:docker-compose restart
小问题
刷新页面没有生效
重启Halo后兴冲冲的刷新文章页,居然完全没有变化!这是由于我们之前的浏览器已经有了缓存记录,不会主动刷新。我们可以在开发者模式下删除当前网站的本地记录来重置,现在,【动态主色】已经能正确捕捉封面的颜色啦。
一个偷懒的解法...
既然无法获取跨域资源的图片,那不如直接用Halo原生的本地附件策略,【动态主色】也能正确捕捉封面的主色。(变成同源资源请求,当然不会被跨域策略拦截)