OSS 跨域配置

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 经常遇到有跨域的问题,老生长谈,却又屡禁不止,谈到跨域我们就了解下它是什么?(以下数据均为模拟数据,屏蔽了真实用户数据)

作者:张医博


什么是 “跨域”


一句话简单说明
一个资源请求一个其它域名的资源时会发起一个跨域 HTTP 请求 (cross-origin HTTP request)。比如说,域名 A http://domaina.example 的某 Web 应用通过 <img> 标签引入了域名 B:http://domainb.foo 的某图片资源 http://domainb.foo/image.jpg,域名 A 的 Web 应用会触发浏览器发起一个跨域 HTTP 请求。


demo


http://www. class="hljs-number">123.com/index.html 调用 http://www. class="hljs-number">123.com/server.php (非跨域)

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。


跨域请求标识


origin ,当浏览器识别出 client 发起的请求需要转到另外一个域名上处理是,会在请求的 request header 中增加一个 origin 标识,如下我用 curl 测试了一个域名。


curl -voa http: class="hljs-comment">//mo-im.oss-cn-beijing.aliyuncs.com/stu_avatar/010/personal.jpg -H "Origin:www.mobby.cn"
% Total % Received % Xferd Average Speed Time Time Time Current
                             Dload  Upload   Total   Spent    Left  Speed

0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 59.110.190.173...

TCP_NODELAY setConnected to mo-im.oss-cn-beijing.aliyuncs.com (59.110.190.173) port 80 (#0)
> GET /stu_avatar/010/personal.jpg HTTP/1.1

> Host: mo-im.oss-cn-beijing.aliyuncs.com
> User-Agent: curl/7.54.0
> Accept: /
> Origin:www.mo.cn
>
< HTTP/1.1 200 OK
< Server: AliyunOSS
< Date: Sun, 09 Sep 2018 12:30:28 GMT
< Content-Type: image/jpeg
< Content-Length: 8407
< Connection: keep-alive
< x-oss-request-id:
< Access-Control-Allow-Origin: www.mobby.cn
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET, POST, HEAD
< Access-Control-Max-Age: 0
< Accept-Ranges: bytes


可以看到挡我发起 Origin 的请求头后,如果目标的网页服务允许来源的域名访问,就会在响应的 Response header 中带上跨域的响应头。(以下 header 目标域名如果设置了才会有响应)


< Access-Control-Allow-Origin: www.mobby.cn (允许的跨域来源,可以写 *,或者绝对域名) 
< Access-Control-Allow-Headers: *(允许跨域时携带哪些 header )
< Access-Control-Allow-Methods: GET, POST, HEAD (允许哪些跨域请求方法,origin 是默认支持的)

常见案例分析


场景 一:CDN 访问 CDN 跨域被拦截


1


通过报错可以看出来 发起跨区域请求的源头 是 bo3.ai.com 加载了 www.ai.com 网站的资源,这两个域名都在 阿里云 cdn 加速。既然找到了请求目的 www.ai.com,那么直接检查下目的域名上是否新增了跨域头。这种情况基本都是目的域名没有加上允许的跨域头导致。


场景二:直传 OSS 引用 CDN 资源被拦截


1


用户直接上传到 OSS ,但是应用了 CDN 的域名时出现的跨域的报错,出现这种情况因为引用的 CDN 上没有配置跨域的属性所以报错,在 CDN 上配置好跨域参数后问题解决。
找到阿里 CDN 控制台对应的 CDN 域名,配置 http header 头,增加三个属性,如下:


2


场景三:CDN 回源到 OSS


2


这个问题比较特殊,拆分两部分说明;
出现这种情况,通过截图我们发现用户有两种请求,分别是 GETPOST 两种,由于 GET 好测试,我们先说 GET


GET:


出现跨域错误,首先就要检查原是否添加了跨域头,于是我们使用 curl 测试一下,如果最简单的 get 测试成功返回跨域头,说明目的 域名设置了跨域响应,如果测试失败说明原没有添加跨域响应。通过如下图很显然看到了目的添加了跨域头。


curl -voa http://mo-im.oss-cn-beijing.aliyuncs.com/stu_avatar/ class="hljs-number">010/personal.jpg -H "Origin:www.mo.cn"
% Total % Received % Xferd Average Speed Time Time Time Current
                             Dload  Upload   Total   Spent    Left  Speed

0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 59.110.190.173...

TCP_NODELAY setConnected to mo-im.oss-cn-beijing.aliyuncs.com () port 80 (#0)
> GET /stu_avatar/010/personal.jpg HTTP/1.1

> Host: mo-im.oss-cn-beijing.aliyuncs.com
> User-Agent: curl/7.54.0
> Accept: /
> Origin:www.mo.cn
>
< HTTP/1.1 200 OK
< Server: AliyunOSS
< Date: Sun, 09 Sep 2018 12:30:28 GMT
< Content-Type: image/jpeg
< Content-Length: 8407
< Connection: keep-alive
< x-oss-request-id: 5B951264980F8FDB749972B3
< Access-Control-Allow-Origin: www.mo.cn
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET, POST, HEAD
< Access-Control-Max-Age: 0


POST


通过 GET 测试发现 oss 是加了跨域头的,但是为什么 POST 请求就返回 405 呢?没有任何跨域头呢?用户反馈为什么手动 curl 测试也是失败。


curl -v -X POST -d '{"user":"xxx"}' http: class="hljs-comment">//mo-im.oss-cn-beijing.aliyuncs.com/stu_avatar/010/personal.jpg -H "Origin:www.mo.cn"
Note: Unnecessary use of -X or --request, POST is already inferred.
Trying 59.110.190.173...TCP_NODELAY setConnected to mo-im.oss-cn-beijing.aliyuncs.com (59.110.190.173) port 80 (#0)
> POST /stu_avatar/010/personal.jpg HTTP/1.1

> Host: mo-im.oss-cn-beijing.aliyuncs.com
> User-Agent: curl/7.54.0
> Accept: /
> Origin:www.mo.cn
> Content-Length: 14
> Content-Type: application/x-www-form-urlencoded
>

upload completely sent off: 14 out of 14 bytes
< HTTP/1.1 405 Method Not Allowed

< Server: AliyunOSS
< Date: Sun, 09 Sep 2018 13:06:28 GMT
< Content-Type: application/xml
< Content-Length: 337
< Connection: keep-alive
< x-oss-request-id:
< Allow: GET DELETE HEAD PUT POST OPTIONS
<
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>MethodNotAllowed</Code>
<Message>The specified method is not allowed against this resource.</Message>
<RequestId></RequestId>
<HostId>mo-im.oss-cn-beijing.aliyuncs.com</HostId>
<Method>POST</Method>
<ResourceType>Object</ResourceType>
</Error>



结论:



  • 请求的格式不是 RFC 标准规定的 content-type:multipart/form-data;
  • 请求头不是内容不是 RFC 规定的表单域提交;
  • 既然不是表单域,那么 OSS API 要求的 filename 参数肯定也不是放在最后一个选项。

JAVA 跨域请求源码


package com.alibaba.edas.carshop.OSS;

import javax.activation.MimetypesFileTypeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

public class OSSPostFile {

<span class="hljs-comment">// The local file path to upload.</span>
<span class="hljs-keyword">private</span> String localFilePath = <span class="hljs-string">"C:\\T\\1.txt"</span>;
<span class="hljs-comment">// OSS domain, such as http://oss-cn-hangzhou.aliyuncs.com</span>
<span class="hljs-keyword">private</span> String endpoint = <span class="hljs-string">"http://oss-cn-beijing.aliyuncs.com"</span>;
<span class="hljs-comment">// Access key Id. Please get it from https://ak-console.aliyun.com</span>
<span class="hljs-keyword">private</span> String accessKeyId = <span class="hljs-string">""</span>;
<span class="hljs-keyword">private</span> String accessKeySecret = <span class="hljs-string">""</span>;
<span class="hljs-comment">// The existing bucket name</span>
<span class="hljs-keyword">private</span> String bucketName = <span class="hljs-string">"您自己的bucket名称"</span>;
<span class="hljs-comment">// The key name for the file to upload.</span>
<span class="hljs-keyword">private</span> String key = <span class="hljs-string">"1.txt"</span>;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">PostObject</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{
    <span class="hljs-comment">// append the 'bucketname.' prior to the domain, such as</span>
    <span class="hljs-comment">// http://bucket1.oss-cn-hangzhou.aliyuncs.com.</span>
    String urlStr = endpoint.replace(<span class="hljs-string">"http://"</span>, <span class="hljs-string">"http://"</span> + bucketName + <span class="hljs-string">"."</span>);

    <span class="hljs-comment">// form fields</span>
    Map&lt;String, String&gt; formFields = <span class="hljs-keyword">new</span> LinkedHashMap&lt;String, String&gt;();

    <span class="hljs-comment">// key</span>
    formFields.put(<span class="hljs-string">"key"</span>, <span class="hljs-keyword">this</span>.key);
    <span class="hljs-comment">// Content-Disposition</span>
    formFields.put(<span class="hljs-string">"Content-Disposition"</span>, <span class="hljs-string">"attachment;filename="</span> + localFilePath);
    <span class="hljs-comment">// OSSAccessKeyId</span>
    formFields.put(<span class="hljs-string">"OSSAccessKeyId"</span>, accessKeyId);
    <span class="hljs-comment">// policy</span>
    String policy = <span class="hljs-string">"{\"expiration\": \"2120-01-01T12:00:00.000Z\",\"conditions\": [[\"content-length-range\", 0, 104857600000]]}"</span>;
    String encodePolicy = <span class="hljs-keyword">new</span> String(Base64.encodeBase64(policy.getBytes()));
    formFields.put(<span class="hljs-string">"policy"</span>, encodePolicy);
    <span class="hljs-comment">// Signature</span>
    String signaturecom = computeSignature(accessKeySecret, encodePolicy);
    formFields.put(<span class="hljs-string">"Signature"</span>, signaturecom);

    String ret = formUpload(urlStr, formFields, localFilePath);

    System.out.println(<span class="hljs-string">"Post Object ["</span> + <span class="hljs-keyword">this</span>.key + <span class="hljs-string">"] to bucket ["</span> + bucketName + <span class="hljs-string">"]"</span>);
    System.out.println(<span class="hljs-string">"post reponse:"</span> + ret);
}

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">computeSignature</span><span class="hljs-params">(String accessKeySecret, String encodePolicy)</span>
        <span class="hljs-keyword">throws</span> UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException </span>{
    <span class="hljs-comment">// convert to UTF-8</span>
    <span class="hljs-keyword">byte</span>[] key = accessKeySecret.getBytes(<span class="hljs-string">"UTF-8"</span>);
    <span class="hljs-keyword">byte</span>[] data = encodePolicy.getBytes(<span class="hljs-string">"UTF-8"</span>);

    <span class="hljs-comment">// hmac-sha1</span>
    Mac mac = Mac.getInstance(<span class="hljs-string">"HmacSHA1"</span>);
    mac.init(<span class="hljs-keyword">new</span> SecretKeySpec(key, <span class="hljs-string">"HmacSHA1"</span>));
    <span class="hljs-keyword">byte</span>[] sha = mac.doFinal(data);

    <span class="hljs-comment">// base64</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> String(Base64.encodeBase64(sha));
}

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">formUpload</span><span class="hljs-params">(String urlStr, Map&lt;String, String&gt; formFields, String localFile)</span> <span class="hljs-keyword">throws</span> Exception </span>{
    String res = <span class="hljs-string">""</span>;
    HttpURLConnection conn = <span class="hljs-keyword">null</span>;
    String boundary = <span class="hljs-string">"9431149156168"</span>;

    <span class="hljs-keyword">try</span> {
        URL url = <span class="hljs-keyword">new</span> URL(urlStr);
        conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(<span class="hljs-number">5000</span>);
        conn.setReadTimeout(<span class="hljs-number">30000</span>);
        conn.setDoOutput(<span class="hljs-keyword">true</span>);
        conn.setDoInput(<span class="hljs-keyword">true</span>);
        conn.setRequestMethod(<span class="hljs-string">"POST"</span>);
        conn.setRequestProperty(<span class="hljs-string">"User-Agent"</span>, <span class="hljs-string">"Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)"</span>);
        conn.setRequestProperty(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"multipart/form-data; boundary="</span> + boundary);
        OutputStream out = <span class="hljs-keyword">new</span> DataOutputStream(conn.getOutputStream());

        <span class="hljs-comment">// text</span>
        <span class="hljs-keyword">if</span> (formFields != <span class="hljs-keyword">null</span>) {
            StringBuffer strBuf = <span class="hljs-keyword">new</span> StringBuffer();
            Iterator&lt;Entry&lt;String, String&gt;&gt; iter = formFields.entrySet().iterator();
            <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;

            <span class="hljs-keyword">while</span> (iter.hasNext()) {
                Entry&lt;String, String&gt; entry = iter.next();
                String inputName = entry.getKey();
                String inputValue = entry.getValue();

                <span class="hljs-keyword">if</span> (inputValue == <span class="hljs-keyword">null</span>) {
                    <span class="hljs-keyword">continue</span>;
                }

                <span class="hljs-keyword">if</span> (i == <span class="hljs-number">0</span>) {
                    strBuf.append(<span class="hljs-string">"--"</span>).append(boundary).append(<span class="hljs-string">"\r\n"</span>);
                    strBuf.append(<span class="hljs-string">"Content-Disposition: form-data; name=\""</span> + inputName + <span class="hljs-string">"\"\r\n\r\n"</span>);
                    strBuf.append(inputValue);
                } <span class="hljs-keyword">else</span> {
                    strBuf.append(<span class="hljs-string">"\r\n"</span>).append(<span class="hljs-string">"--"</span>).append(boundary).append(<span class="hljs-string">"\r\n"</span>);
                    strBuf.append(<span class="hljs-string">"Content-Disposition: form-data; name=\""</span> + inputName + <span class="hljs-string">"\"\r\n\r\n"</span>);
                    strBuf.append(inputValue);
                }

                i++;
            }
            out.write(strBuf.toString().getBytes());
        }

        StringBuffer strBuf1 = <span class="hljs-keyword">new</span> StringBuffer();
        String callback = <span class="hljs-string">"{\"callbackUrl\":\"http://47.93.116.168/Revice.ashx\",\"callbackBody\":\"{\\\"bucket\\\"=${bucket},\\\"size\\\"=${size}}\"}"</span>;

        <span class="hljs-keyword">byte</span>[] textByte = callback.getBytes(<span class="hljs-string">"UTF-8"</span>);
        strBuf1.append(<span class="hljs-string">"\r\n"</span>).append(<span class="hljs-string">"--"</span>).append(boundary).append(<span class="hljs-string">"\r\n"</span>);

        String callbackstr = <span class="hljs-keyword">new</span> String(Base64.encodeBase64(textByte));
        strBuf1.append(<span class="hljs-string">"Content-Disposition: form-data; name=\"callback\"\r\n\r\n"</span> + callbackstr + <span class="hljs-string">"\r\n\r\n"</span>);
        out.write(strBuf1.toString().getBytes());

        <span class="hljs-comment">// file</span>
        File file = <span class="hljs-keyword">new</span> File(localFile);
        String filename = file.getName();
        String contentType = <span class="hljs-keyword">new</span> MimetypesFileTypeMap().getContentType(file);
        <span class="hljs-keyword">if</span> (contentType == <span class="hljs-keyword">null</span> || contentType.equals(<span class="hljs-string">""</span>)) {
            contentType = <span class="hljs-string">"application/octet-stream"</span>;
        }

        StringBuffer strBuf = <span class="hljs-keyword">new</span> StringBuffer();
        strBuf.append(<span class="hljs-string">"\r\n"</span>).append(<span class="hljs-string">"--"</span>).append(boundary).append(<span class="hljs-string">"\r\n"</span>);
        strBuf.append(<span class="hljs-string">"Content-Disposition: form-data; name=\"file\"; "</span> + <span class="hljs-string">"filename=\""</span> + filename + <span class="hljs-string">"\"\r\n"</span>);
        strBuf.append(<span class="hljs-string">"Content-Type: "</span> + contentType + <span class="hljs-string">"\r\n\r\n"</span>);
        out.write(strBuf.toString().getBytes());

        DataInputStream in = <span class="hljs-keyword">new</span> DataInputStream(<span class="hljs-keyword">new</span> FileInputStream(file));
        <span class="hljs-keyword">int</span> bytes = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">byte</span>[] bufferOut = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[<span class="hljs-number">1024</span>];
        <span class="hljs-keyword">while</span> ((bytes = in.read(bufferOut)) != -<span class="hljs-number">1</span>) {
            out.write(bufferOut, <span class="hljs-number">0</span>, bytes);
        }
        in.close();

        <span class="hljs-keyword">byte</span>[] endData = (<span class="hljs-string">"\r\n--"</span> + boundary + <span class="hljs-string">"--\r\n"</span>).getBytes();
        out.write(endData);
        out.flush();
        out.close();

        <span class="hljs-comment">// Gets the file data</span>
        strBuf = <span class="hljs-keyword">new</span> StringBuffer();
        BufferedReader reader = <span class="hljs-keyword">new</span> BufferedReader(<span class="hljs-keyword">new</span> InputStreamReader(conn.getInputStream()));
        String line = <span class="hljs-keyword">null</span>;
        <span class="hljs-keyword">while</span> ((line = reader.readLine()) != <span class="hljs-keyword">null</span>) {
            strBuf.append(line).append(<span class="hljs-string">"\n"</span>);
        }
        res = strBuf.toString();
        reader.close();
        reader = <span class="hljs-keyword">null</span>;
    } <span class="hljs-keyword">catch</span> (Exception e) {
        System.err.println(<span class="hljs-string">"Send post request exception: "</span> + e.getLocalizedMessage());
        <span class="hljs-keyword">throw</span> e;
    } <span class="hljs-keyword">finally</span> {
        <span class="hljs-keyword">if</span> (conn != <span class="hljs-keyword">null</span>) {
            conn.disconnect();
            conn = <span class="hljs-keyword">null</span>;
        }
    }

    <span class="hljs-keyword">return</span> res;
}

}


测试结果


6


场景四:


OSS 控制台配置跨域规则失败


image


初步分析:出现的原因是因为用户之前历史配置过的规则中含有特殊字符导致控制台拉取是否有历史配置时失败,响应了 invalidresponse。


解决方法:用户通过 SDK 修改跨域规则,通过 SDK 配置的规则将历史规则覆盖掉。


场景五:客户端使用 cavens 测试图片的跨域访问被 403


image


1、先确认 OSS 是否非配置了跨域头,配置的是否正确;


image


2、出现类似问题可以使用 postman 或者 curl 工具测试,看下是否同样出现问题。


image


3、如果发现本地测试跨域头都是正常的,只有客户端的浏览器测试异常,请用户清除浏览器缓存,开启隐私模式进行测试。


相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
相关文章
|
26天前
|
安全 API 开发工具
oss加密的配置方法
阿里云OSS提供多种加密选项:SSE-OSS(默认或对象级AES-256加密)、SSE-KMS(使用KMS托管CMK)、临时密钥加密和客户端加密(CSE)。可通过控制台或API设置Bucket策略,使用HTTP头部指定加密方式。KMS和临时密钥可能涉及更复杂的密钥管理和权限配置。
117 5
|
27天前
|
存储 Cloud Native API
oss云网关配置
配置阿里云OSS与云网关实现灵活数据传输和访问控制。步骤包括开通OSS服务,创建Bucket,获取访问凭证,可选配置CORS和生命周期规则。云网关配置涉及阿里云云原生网关的代理规则设定或使用云存储网关集成OSS访问,具体配置需参照产品文档,因产品更新可能会有变动。
19 1
|
29天前
|
存储 缓存 安全
oss跨域资源共享(CORS Configuration)
oss跨域资源共享(CORS Configuration)
46 4
|
3月前
|
存储 Java 对象存储
springboot配置阿里云OSS存储实现文件上传下载功能
【1月更文挑战第1天】springboot配置阿里云OSS存储实现文件上传下载功能
508 2
|
3月前
|
Cloud Native Java 开发工具
云原生 阿里云分布式文件系统 对象存储OSS 服务配置
【1月更文挑战第8天】云原生 阿里云分布式文件系统 对象存储OSS 服务配置
|
8月前
|
存储 运维 数据安全/隐私保护
【运维知识进阶篇】用阿里云部署kod可道云网盘(配置Redis+MySQL+NAS+OSS)(四)
【运维知识进阶篇】用阿里云部署kod可道云网盘(配置Redis+MySQL+NAS+OSS)(四)
205 0
|
1月前
|
存储 弹性计算 安全
oss配置Bucket属性
oss配置Bucket属性
59 1
|
2月前
|
SQL 分布式计算 DataWorks
maxcompute配置问题之连接oss报错如何解决
MaxCompute配置是指在使用阿里云MaxCompute服务时对项目设置、计算资源、存储空间等进行的各项调整;本合集将提供MaxCompute配置的指南和建议,帮助用户根据数据处理需求优化其MaxCompute环境。
29 0
|
6月前
|
存储 安全 API
PicGo配置阿里云oss
PicGo配置阿里云oss
217 0
|
6月前
|
开发工具 对象存储 C++
UE4 x 阿里云OSS 配置问题和解决方案
在OSS配置到UE4时遇到一些问题,已解决,分享一些解决方案。
1192 0