随笔 - 59  文章 - 20  评论 - 61  阅读 - 85494

Net Core项目通过Amazon SDK访问天翼云对象存储服务OOS问题

Net Core项目通过Amazon SDK访问天翼云对象存储服务OOS问题 

背景

近期在阿里云运营的Net Core项目应客户要求需要部署到天翼云,其中比较麻烦的事情是把阿里云对象存储服务OSS改为天翼云对象存储服务OOS,搞了3天,折腾个半死,好不容易才搞定了,赶紧记录下来。

天翼云的对象存储服务有3种,经典I型,经典II型,融合版。我的项目需求比较简单,就是一些数据文件不适合存数据库的,就用OSS存起来,一般的OSS都能用。简单对比了一下,发现经典I型,经典II型都没有.NetSDK,融合版有.NetSDK,为了方便快速开发就用融合版吧。然后在创建融合版的存储桶的时候,提示已售完,晕死。

发个了工单问客服,推荐我用经典I型,就这么办吧。在经典I型开通OOS,创建桶,购买资源包,创建访问Key备用。

 

第一关:通过Web Api访问天翼云OOS拼接认证头签名不对

学习天翼云通过Web Api访问OOS的文档,比如PUT上传一个文件,结构是简单的

https://www.ctyun.cn/document/10026693/10027315

 

但是,Authorization需要根据上传内容动态拼接出来,非常复杂,看得我头顶冒青烟。

https://www.ctyun.cn/document/10026693/10027058

验证码计算方法如下:

 

"Authorization: AWS" + AccessId + ":" + Signature

Signature =Base64( HMAC-SHA1( YourSecretAccessKey, UTF-8-Encoding-Of( StringToSign ) ) ) ;

StringToSign = HTTP-VERB + "\n" +

Content-MD5 + "\n" +

Content-Type + "\n" +

Date + "\n" +

CanonicalizedAmzHeaders +"\n" +

CanonicalizedResource;

 

CanonicalizedAmzHeaders = [详见下列描述];

CanonicalizedResource = [ "/" + Bucket ] +

<HTTP-Request-URI > +

  [<sub-resource>]

 

PUT Object

 

向名为johnsmithbucket中上传一个对象。

请求

StringToSign

PUT /photos/puppy.jpg   HTTP/1.1

Content-Type:   image/jpeg

Content-Length: 94328

Host: johnsmith.oos-cn.ctyunapi.cn

Date: Tue, 27 Mar 2007   21:15:45 +0000

Authorization: AWS 7799e793ce4624ee7e5a:hcicpDDvL9SsO6AkvxqmIWkmOuQ=

PUT\n

\n

image/jpeg\n

Tue,   27 Mar 2007 21:15:45 +0000\n

/johnsmith/photos/puppy.jpg

注意:Content-Type请求头包含在请求中,也包含在StringToSign中。但请求中没有Content-MD5请求头,所以StringToSign中是空行。

 

也参考了天翼云OOS通过java创建认证头的例子。

https://www.ctyun.cn/document/10026693/10027543

 

我参考示例拼接认证头,试了很多次,都不成功。通过HttpClient发起调用,服务端总是返回403禁止访问错误,认证签名错误。

<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>

 

在百度上找了很久,只找到一篇文章,作者做了一些封装,拼接认证头,成功访问了天翼云OOS。我用作者的方法,仍然无法解决认证签名错误的问题。

https://www.cnblogs.com/JackXiong/p/10875953.html

_NET CORE 对接天翼云 OOS - 懒惰的jacky - 博客园.html

 

第二关:通过Amazon SDK访问OOS认证头签名不对

Web Api这条路走不通,还有别的路可走吗?

再次学习天翼云OSS经典I型的资料,发现了一个重要信息:兼容Amazon S3标准!

https://www.ctyun.cn/document/10026693/10026712

天翼云对象存储(经典版)I型(Object-Oriented Storage,以下简称OOS)是中国电信为客户提供的一种海量、弹性、高可用、高性价比的对象存储服务。客户只需花极少的钱就可以获得一个几乎无限的存储空间,可以随时根据需要调整对资源的占用,并只需为真正使用的资源付费。OOS提供了基于Web门户和基于REST接口两种访问方式,用户可以在任何地方通过互联网对数据进行管理和访问。OOS提供的REST接口与Amazon S3兼容,因此基于OOS的业务可以非常轻松的与Amazon S3对接。用户还可以使用OOS提供迁移工具轻松地实现海量数据的移入与移出。

 

既然如此,那么客户端是不是可以通过Amazon S3的库,实现对天翼云OOS的访问呢?这样就不用自己拼接认证头了,SDK本来就干这个的啊!而且,在网上可以找到相当多的Amazon S3 Net core SDK访问云服务的资料,Amazon.Net还是很友好的。天翼云OOS融合版也有.NetSDK,经过仔细研究,它用的就是AmazonSDK

https://console.xstore.ctyun.cn/doc/store/sdk/dotnet/quickUse.html

安装SDK

在天翼云官网下载,下载地址:OSS_DOTNET_SDK.zip

修改项目的csproj文件,在<PropertyGroup>中增加以下内容。

<PropertyGroup>

<RestoreSources>$(RestoreSources);filePathToPackage</RestoreSources>

</PropertyGroup>

其中filePathToPackage指的是OSS_DOTNET_SDK.zip解压后的径。然后在项目csproj文件所在目录下执行dotnet命令安装依赖包:

dotnet add package AWSSDK.S3 --version 3.7.0.18

# 使用sts服务需要添加以下依赖

dotnet add package AWSSDK.SecurityToken --version 3.7.1.6

dotnet restore

 

参考PUT上传对象的示例写代码即可,基本上照搬即可。

https://console.xstore.ctyun.cn/doc/store/sdk/dotnet/objectOperation/putObject.html

 

另外,天翼云官网OOS融合版也提供了.Net SDK源代码。

https://console.xstore.ctyun.cn/doc/store/sdk/introduction.html

gitee下载它的代码学习一下。

https://gitee.com/ctyun-xstore/ctyun-xstore-sdk-demo

它里边有一段初始化代码可以借鉴一下

D:\software\test\ctyun-xstore-sdk-demo-master\oss-csharp-demo\oss-csharp-demo\S3Demo.cs

复制代码
public S3Demo()
        {
            Amazon.AWSConfigs.LoggingConfig.LogTo = Amazon.LoggingOptions.Console;
            Amazon.AWSConfigs.LoggingConfig.LogResponses = Amazon.ResponseLoggingOption.Always;

            var credentials = new BasicAWSCredentials(Config.AccessKey, Config.SecretKey);
            var conf = new AmazonS3Config
            {
                ServiceURL = Config.EndPoint,
                AuthenticationRegion = "cn",
                Timeout = TimeSpan.FromSeconds(10),
                ReadWriteTimeout = TimeSpan.FromSeconds(10),
                RetryMode = RequestRetryMode.Standard,
                MaxErrorRetry = 2,
            };
            this.s3Client = new AmazonS3Client(credentials, conf);
        }
复制代码

我参照以上示例写了测试DEMO,运行报错,还好错误提示很清晰,认证头的region期望是cn,实际上是us-east-1。但是我明明设置AuthenticationRegion = "cn"了,为什么没有生效呢?

<Error><Code>AuthorizationHeaderMalformed</Code><Message>The authorization header is malformed. the region 'us-east-1' is wrong; expecting 'cn'</Message>

通过Fiddler抓包也非常清晰的看到,认证头的region确实是us-east-1不对。

Authorization: AWS4-HMAC-SHA256 Credential=687e486556264706fbc7/20230210/us-east-1/s3/aws4_request, SignedHeaders=content-length;content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length, Signature=9b6...

多次检查后才发现,我的域名写错了,类似PUT http://bucketname.bucketname.oos-cn.ctyunapi.cn/test.obj HTTP/1.1,把路由修正之后,region问题解决了。

Authorization: AWS4-HMAC-SHA256 Credential=687e486556264706fbc7/20230210/cn/s3/aws4_request, SignedHeaders=content-length;content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length, Signature=590

但是访问天翼云OOS仍然返回SignatureDoesNotMatch认证签名不对!连SDK签名都不对,我还能怎么办?只好再给天翼云提工单。

 

第三关:设置了正确的region

天翼云的客服响应很快,经过和技术客服沟通,对方告知region必须设置为具体的地点,比如山东青岛sdqd,不能是cn,首先得修改OOS容器的区域设置,指定固定地点,不允许系统自动调度。

然后把访问OOSEndPoint域名,认证region都改为sdgq,例如:

EndPoint=https://oos-sdqd.ctyunapi.cn

AuthenticationRegion = "sdqd"

 

再次测试,抓包看发送信息,域名,region都符合要求了。

PUT https://xxx.oos-sdqd.ctyunapi.cn/test.obj HTTP/1.1

Expect: 100-continue

User-Agent: aws-sdk-dotnet-coreclr/3.7.0.18 aws-sdk-dotnet-core/3.7.0.17 .NET_Core/7.0.1 OS/Microsoft_Windows_10.0.22000 ClientAsync

amz-sdk-invocation-id: e98f3de2-23ab-4cbb-8454-175b6015686d

amz-sdk-request: attempt=1; max=5

Host: xxx.oos-sdqd.ctyunapi.cn

X-Amz-Date: 20230210T073839Z

X-Amz-Decoded-Content-Length: 10240

X-Amz-Content-SHA256: STREAMING-AWS4-HMAC-SHA256-PAYLOAD

Authorization: AWS4-HMAC-SHA256 Credential=687e486556264706fbc7/20230210/sdqd/s3/aws4_request, SignedHeaders=content-length;content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length, Signature=0dc...

traceparent: 00-74306b9e29bb01800d7ad7ff09bc293b-753f784ed4c13320-00

Content-Length: 10415

Content-Type: application/octet-stream

 

但是访问天翼云OOS仍然返回SignatureDoesNotMatch认证签名不对!

这次天翼云的技术客服也没招了,告知天翼云OOS不兼容Amazon S3SDK。如果还想继续打通Amazon S3 SDK这条路,我只能靠自己了。

 

第四关:降级使用版本2认证头

百度,客服都无路可走了。折腾了这么久,元气已经耗尽了,头顶已经处于冒火状态。奥密克戎都没把我带走,这个难题差点要了我这半条老命。

冷静下来想了一下,还有活路。在VS2022NuGet里边,搜索天翼云相关的库,勾选预览版,以关键字ctyun搜索,没有任何结果,真是绝了。

最后一条活路,在GitHub里边搜索ctyun,居然找到一个C# netcore的仓库!终于体验到“垂死病中惊坐起”的感觉。无比感谢大佬。

https://github.com/mn-s/ctyun-oos-net-core-sdk

 

我下载了代码,填上自己的天翼云OOS参数,运行一下PUT上传对象,居然成功了!卧槽!!最后一线希望居然成功了。

赶紧Fiddler抓包看发送的数据,原来用的是版本2的认证头!难怪了。

PUT https://xxx.oos-sdqd.ctyunapi.cn/test.obj HTTP/1.1

Host: xxx.oos-sdqd.ctyunapi.cn

Expect: 100-continue

User-Agent: aws-sdk-dotnet-coreclr/3.3.104.6 aws-sdk-dotnet-core/3.3.103.18 .NET_Core/7.0.1 OS/Microsoft_Windows_10.0.22000 ClientAsync

X-Amz-Date: Fri, 10 Feb 2023 09:31:49 GMT

Authorization: AWS 687e486556264706fbc7:AvJwhPrQ3B50IhrHMIsuXq62VNY=

Content-Length: 20

Content-Type: application/octet-stream

 

Ctyun OOS SDK for C#

 

HTTP/1.1 200 OK

Date: Fri, 10 Feb 2023 09:31:51 GMT

x-amz-request-id: 102c12aa50c34c8cb92c1f2e23303238f7f901edeff1f3f5f7

ETag: "847259bd5a9bb8fd1c13b17f1253229b"

Content-Length: 0

Server: CTYUN

 

至此,问题原因也非常清楚了,天翼云OOS只支持版本2认证头,不支持版本4认证头。

但是,我在修改我写的DEMO的时候,还是走了很多弯路。

我直接修改配置对象的SignatureVersion = "2"测试发现仍然采用了版本4的认证头。我百思不得其解,抄ctyun-oos-net-core-sdk的代码,甚至引用他的类库,都不行,明明已经胜利在望了,却够不着。

var conf = new AmazonS3Config

    {

        ServiceURL = EndpointUrl,

AuthenticationRegion = "sdqd",

SignatureVersion = "2",

    };

经过仔细对比,终于发现一个疑点,ctyun-oos-net-core-sdk并没有设置AuthenticationRegion,也能够通过认证,我设置了反而通不过,决定删除AuthenticationRegion = "sdqd"这行,居然就通过了!这真是没天理啊!

我理解为,如果设置了AuthenticationRegionAmazon S3 SDK就自动采用版本4认证,因此,千万不要设置AuthenticationRegion

 

至此,实现了通过Amazon S3 SDK访问天翼云OSS经典I类型对象存储服务。

 

总结一下代码要点

NuGet安装

<PackageReference Include="AWSSDK.S3" Version="3.7.103.6" />

 

初始化AmazonS3Client客户端对象,

        var credentials = new BasicAWSCredentials(AccessKeyId, AccessKeySecret);

        var conf = new AmazonS3Config

        {

            ServiceURL = $"https://xxx.oos-sdqd.ctyunapi.cn”,

 

            //为了使用版本2认证,千万不能设置认证区域!否则会采用版本4,天翼云不支持版本4认证!

            //AuthenticationRegion = "sdqd",

 

            SignatureVersion = "2",

        };

 

        s3Client = new AmazonS3Client(credentials, conf);

 

PUT上传对象

public async Task<bool> PutObjectAsync(string objectName, Stream stream)

    {

        var req = new PutObjectRequest()

        {

            BucketName = _ossConfig.BucketName,

            Key = objectName,

            InputStream = stream,

        };

 

        var res = await s3Client.PutObjectAsync(req);

 

        if (res.HttpStatusCode == System.Net.HttpStatusCode.OK)

        {

            return true;

        }

        else

        {

            return false;

        }

}

 

posted on   SunnyTrudeau  阅读(1280)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示