diff options
author | crupest <crupest@outlook.com> | 2019-04-23 00:14:35 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2019-04-23 00:14:35 +0800 |
commit | 797b1da15c76f6598dcc48f675c1b82cb27a17ed (patch) | |
tree | 99754727d79f8ae27c96f35e2541d51be780deb4 | |
parent | 752850049301290b991f748816c66352f578c057 (diff) | |
download | timeline-797b1da15c76f6598dcc48f675c1b82cb27a17ed.tar.gz timeline-797b1da15c76f6598dcc48f675c1b82cb27a17ed.tar.bz2 timeline-797b1da15c76f6598dcc48f675c1b82cb27a17ed.zip |
Remove qcloud cs sdk. I will write one by myself.
Develop signature algorithm.
-rw-r--r-- | Timeline.Tests/QCloudCosServiceUnitTest.cs | 41 | ||||
-rw-r--r-- | Timeline/Configs/QCloudCosConfig.cs (renamed from Timeline/Configs/TencentCosConfig.cs) | 7 | ||||
-rw-r--r-- | Timeline/Services/TencentCloudCosService.cs | 174 | ||||
-rw-r--r-- | Timeline/Services/UserService.cs | 6 | ||||
-rw-r--r-- | Timeline/Startup.cs | 4 | ||||
-rw-r--r-- | Timeline/Timeline.csproj | 1 |
6 files changed, 157 insertions, 76 deletions
diff --git a/Timeline.Tests/QCloudCosServiceUnitTest.cs b/Timeline.Tests/QCloudCosServiceUnitTest.cs new file mode 100644 index 00000000..c02f70be --- /dev/null +++ b/Timeline.Tests/QCloudCosServiceUnitTest.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using Timeline.Services; +using Xunit; + +namespace Timeline.Tests +{ + public class QCloudCosServiceUnitTest + { + [Fact] + public void GenerateSignatureTest() + { + var credential = new QCloudCosService.QCloudCredentials + { + SecretId = "AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q", + SecretKey = "BQYIM75p8x0iWVFSIgqEKwFprpRSVHlz" + }; + + var request = new QCloudCosService.RequestInfo + { + Method = "put", + Uri = "/exampleobject", + Parameters = new Dictionary<string, string>(), + Headers = new Dictionary<string, string> + { + ["Host"] = "examplebucket-1250000000.cos.ap-beijing.myqcloud.com", + ["x-cos-storage-class"] = "standard", + ["x-cos-content-sha1"] = "b502c3a1f48c8609ae212cdfb639dee39673f5e" + } + }; + + var signValidTime = new QCloudCosService.TimeDuration + { + Start = DateTimeOffset.FromUnixTimeSeconds(1417773892), + End = DateTimeOffset.FromUnixTimeSeconds(1417853898) + }; + + Assert.Equal("q-sign-algorithm=sha1&q-ak=AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q&q-sign-time=1417773892;1417853898&q-key-time=1417773892;1417853898&q-header-list=host;x-cos-content-sha1;x-cos-storage-class&q-url-param-list=&q-signature=0ab12f43e74cbe148d705cd9fae8adc9a6d39cc1", QCloudCosService.GenerateSign(credential, request, signValidTime)); + } + } +} diff --git a/Timeline/Configs/TencentCosConfig.cs b/Timeline/Configs/QCloudCosConfig.cs index c41669f1..6d10436c 100644 --- a/Timeline/Configs/TencentCosConfig.cs +++ b/Timeline/Configs/QCloudCosConfig.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -namespace Timeline.Configs +namespace Timeline.Configs { - public class TencentCosConfig + public class QCloudCosConfig { public string AppId { get; set; } public string Region { get; set; } diff --git a/Timeline/Services/TencentCloudCosService.cs b/Timeline/Services/TencentCloudCosService.cs index 9ab9d54d..1bfcf745 100644 --- a/Timeline/Services/TencentCloudCosService.cs +++ b/Timeline/Services/TencentCloudCosService.cs @@ -1,99 +1,143 @@ -using COSXML; -using COSXML.Auth; -using COSXML.CosException; -using COSXML.Model; -using COSXML.Model.Object; -using COSXML.Model.Tag; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; using System.Threading.Tasks; using Timeline.Configs; namespace Timeline.Services { - public interface ITencentCloudCosService + public interface IQCloudCosService { - Task<bool> Exists(string bucket, string key); + Task<bool> ObjectExists(string bucket, string key); string GetObjectUrl(string bucket, string key); } - public class TencentCloudCosService : ITencentCloudCosService + public class QCloudCosService : IQCloudCosService { - private readonly TencentCosConfig _config; - private readonly CosXmlServer _server; - private readonly ILogger<TencentCloudCosService> _logger; + private readonly QCloudCosConfig _config; + private readonly ILogger<QCloudCosService> _logger; - public TencentCloudCosService(IOptions<TencentCosConfig> config, ILogger<TencentCloudCosService> logger) + public QCloudCosService(IOptions<QCloudCosConfig> config, ILogger<QCloudCosService> logger) { _config = config.Value; _logger = logger; + } - var cosConfig = new CosXmlConfig.Builder() - .IsHttps(true) - .SetAppid(config.Value.AppId) - .SetRegion(config.Value.Region) - .SetDebugLog(true) - .Build(); + public class QCloudCredentials + { + public string SecretId { get; set; } + public string SecretKey { get; set; } + } - var credentialProvider = new DefaultQCloudCredentialProvider(config.Value.SecretId, config.Value.SecretKey, 3600); + public class RequestInfo + { + public string Method { get; set; } + public string Uri { get; set; } + public IEnumerable<KeyValuePair<string, string>> Parameters { get; set; } + public IEnumerable<KeyValuePair<string, string>> Headers { get; set; } + } - _server = new CosXmlServer(cosConfig, credentialProvider); + public class TimeDuration + { + public DateTimeOffset Start { get; set; } + public DateTimeOffset End { get; set; } } - public Task<bool> Exists(string bucket, string key) + public static string GenerateSign(QCloudCredentials credentials, RequestInfo request, TimeDuration signValidTime) { - bucket = bucket + "-" + _config.AppId; + Debug.Assert(credentials != null); + Debug.Assert(credentials.SecretId != null); + Debug.Assert(credentials.SecretKey != null); + Debug.Assert(request != null); + Debug.Assert(request.Method != null); + Debug.Assert(request.Uri != null); + Debug.Assert(request.Parameters != null); + Debug.Assert(request.Headers != null); + Debug.Assert(signValidTime != null); + Debug.Assert(signValidTime.Start < signValidTime.End, "Start must be before End in sign valid time."); + + List<(string key, string value)> Transform(IEnumerable<KeyValuePair<string, string>> raw) + { + var sorted= raw.Select(p => (key: p.Key.ToLower(), value: WebUtility.UrlEncode(p.Value))).ToList(); + sorted.Sort((left, right) => string.CompareOrdinal(left.key, right.key)); + return sorted; + } + + var transformedParameters = Transform(request.Parameters); + var transformedHeaders = Transform(request.Headers); + + List<(string, string)> result = new List<(string, string)>(); + + const string signAlgorithm = "sha1"; + result.Add(("q-sign-algorithm", signAlgorithm)); + + result.Add(("q-ak", credentials.SecretId)); + + var signTime = $"{signValidTime.Start.ToUnixTimeSeconds().ToString()};{signValidTime.End.ToUnixTimeSeconds().ToString()}"; + var keyTime = signTime; + result.Add(("q-sign-time", signTime)); + result.Add(("q-key-time", keyTime)); - var request = new HeadObjectRequest(bucket, key); + result.Add(("q-header-list", string.Join(';', transformedHeaders.Select(h => h.key)))); + result.Add(("q-url-param-list", string.Join(';', transformedParameters.Select(p => p.key)))); - var t = new TaskCompletionSource<bool>(); + HMACSHA1 hmac = new HMACSHA1(); - _server.HeadObject(request, delegate (CosResult result) + string ByteArrayToString(byte[] bytes) { - if (result.httpCode >= 200 && result.httpCode < 300) - t.TrySetResult(true); - else - t.TrySetResult(false); - }, - delegate (CosClientException clientException, CosServerException serverException) + return BitConverter.ToString(bytes).Replace("-", "").ToLower(); + } + + hmac.Key = Encoding.UTF8.GetBytes(credentials.SecretKey); + var signKey = ByteArrayToString(hmac.ComputeHash(Encoding.UTF8.GetBytes(keyTime))); + + string Join(IEnumerable<(string key, string value)> raw) { - if (clientException != null) - { - _logger.LogError(clientException, "An client error occured when test cos object existence. Bucket : {} . Key : {} .", bucket, key); - t.TrySetException(clientException); - return; - } - if (serverException != null) - { - if (serverException.statusCode == 404) - { - t.TrySetResult(false); - return; - } - _logger.LogError(serverException, "An server error occured when test cos object existence. Bucket : {} . Key : {} .", bucket, key); - t.TrySetException(serverException); - return; - } - _logger.LogError("An unknown error occured when test cos object existence. Bucket : {} . Key : {} .", bucket, key); - t.TrySetException(new Exception("Unknown exception when test cos object existence.")); - }); - - return t.Task; + return string.Join('&', raw.Select(p => string.Concat(p.key, "=", p.value))); + } + + var httpString = new StringBuilder() + .Append(request.Method).Append('\n') + .Append(request.Uri).Append('\n') + .Append(Join(transformedParameters)).Append('\n') + .Append(Join(transformedHeaders)).Append('\n') + .ToString(); + + string Sha1(string data) + { + var sha1 = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(data)); + return ByteArrayToString(sha1); + } + + var stringToSign = new StringBuilder() + .Append(signAlgorithm).Append('\n') + .Append(signTime).Append('\n') + .Append(Sha1(httpString)).Append('\n') + .ToString(); + + hmac.Key = Encoding.UTF8.GetBytes(signKey); + var signature = ByteArrayToString(hmac.ComputeHash( + Encoding.UTF8.GetBytes(stringToSign))); + + result.Add(("q-signature", signature)); + + return Join(result); + } + + public Task<bool> ObjectExists(string bucket, string key) + { + throw new NotImplementedException(); } public string GetObjectUrl(string bucket, string key) { - return _server.GenerateSignURL(new PreSignatureStruct() - { - appid = _config.AppId, - region = _config.Region, - bucket = bucket + "-" + _config.AppId, - key = key, - httpMethod = "GET", - isHttps = true, - signDurationSecond = 300 - }); + throw new NotImplementedException(); } } } diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index a444d434..d1555660 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -150,9 +150,9 @@ namespace Timeline.Services private readonly DatabaseContext _databaseContext; private readonly IJwtService _jwtService; private readonly IPasswordService _passwordService; - private readonly ITencentCloudCosService _cosService; + private readonly IQCloudCosService _cosService; - public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IJwtService jwtService, IPasswordService passwordService, ITencentCloudCosService cosService) + public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IJwtService jwtService, IPasswordService passwordService, IQCloudCosService cosService) { _logger = logger; _databaseContext = databaseContext; @@ -301,7 +301,7 @@ namespace Timeline.Services public async Task<string> GetAvatarUrl(string username) { - var exists = await _cosService.Exists("avatar", username); + var exists = await _cosService.ObjectExists("avatar", username); if (exists) return _cosService.GetObjectUrl("avatar", username); else diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs index 6491554a..12d60843 100644 --- a/Timeline/Startup.cs +++ b/Timeline/Startup.cs @@ -81,8 +81,8 @@ namespace Timeline }); }); - services.Configure<TencentCosConfig>(Configuration.GetSection(nameof(TencentCosConfig))); - services.AddSingleton<ITencentCloudCosService, TencentCloudCosService>(); + services.Configure<QCloudCosConfig>(Configuration.GetSection(nameof(QCloudCosConfig))); + services.AddSingleton<IQCloudCosService, QCloudCosService>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj index c9454ec9..93513bd3 100644 --- a/Timeline/Timeline.csproj +++ b/Timeline/Timeline.csproj @@ -12,6 +12,5 @@ <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Design" Version="1.1.2" /> - <PackageReference Include="Tencent.QCloud.Cos.Sdk.NetCore" Version="5.4.1.1" /> </ItemGroup> </Project> |