Net Core项目通过Amazon SDK访问天翼云对象存储服务OOS问题
背景
近期在阿里云运营的Net Core项目应客户要求需要部署到天翼云,其中比较麻烦的事情是把阿里云对象存储服务OSS改为天翼云对象存储服务OOS,搞了3天,折腾个半死,好不容易才搞定了,赶紧记录下来。
天翼云的对象存储服务有3种,经典I型,经典II型,融合版。我的项目需求比较简单,就是一些数据文件不适合存数据库的,就用OSS存起来,一般的OSS都能用。简单对比了一下,发现经典I型,经典II型都没有.Net的SDK,融合版有.Net的SDK,为了方便快速开发就用融合版吧。然后在创建融合版的存储桶的时候,提示已售完,晕死。
发个了工单问客服,推荐我用经典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
向名为johnsmith的bucket中上传一个对象。
请求 |
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融合版也有.Net的SDK,经过仔细研究,它用的就是Amazon的SDK。
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容器的区域设置,指定固定地点,不允许系统自动调度。
然后把访问OOS的EndPoint域名,认证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 S3的SDK。如果还想继续打通Amazon S3 SDK这条路,我只能靠自己了。
第四关:降级使用版本2认证头
百度,客服都无路可走了。折腾了这么久,元气已经耗尽了,头顶已经处于冒火状态。奥密克戎都没把我带走,这个难题差点要了我这半条老命。
冷静下来想了一下,还有活路。在VS2022的NuGet里边,搜索天翼云相关的库,勾选预览版,以关键字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"这行,居然就通过了!这真是没天理啊!
我理解为,如果设置了AuthenticationRegion,Amazon 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;
}
}
标签:Core,cn,天翼云,认证,Amazon,OOS,SDK From: https://www.cnblogs.com/sunnytrudeau/p/17110705.html