aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2019-04-23 00:14:35 +0800
committercrupest <crupest@outlook.com>2019-04-23 00:14:35 +0800
commit797b1da15c76f6598dcc48f675c1b82cb27a17ed (patch)
tree99754727d79f8ae27c96f35e2541d51be780deb4
parent752850049301290b991f748816c66352f578c057 (diff)
downloadtimeline-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.cs41
-rw-r--r--Timeline/Configs/QCloudCosConfig.cs (renamed from Timeline/Configs/TencentCosConfig.cs)7
-rw-r--r--Timeline/Services/TencentCloudCosService.cs174
-rw-r--r--Timeline/Services/UserService.cs6
-rw-r--r--Timeline/Startup.cs4
-rw-r--r--Timeline/Timeline.csproj1
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>