Skip to content

Commit ae32734

Browse files
authored
Optimize Bilibili provider (#1071)
Perf optimization for Bilibili provider for generating HTTP headers.
1 parent 218e032 commit ae32734

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

src/AspNet.Security.OAuth.Bilibili/BilibiliAuthenticationHandler.cs

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,19 @@ private static string ComputeHmacSHA256(string key, string data)
9090
return Convert.ToHexStringLower(hash);
9191
}
9292

93-
private static string BuildSignatureString(HttpRequestMessage request, string appSecret)
93+
private static string BuildSignatureString(SortedList<string, string> xbiliHeaders, string appSecret)
9494
{
95-
var headers = request.Headers
96-
.Where(h => h.Key.StartsWith("x-bili-", StringComparison.OrdinalIgnoreCase))
97-
.OrderBy(h => h.Key)
98-
.Select(h => $"{h.Key}:{string.Join(",", h.Value)}")
99-
.ToList();
100-
101-
var signature = string.Join('\n', headers);
95+
var sb = new StringBuilder(256); // 256 is an estimated size for the plain text
96+
foreach ((var name, var value) in xbiliHeaders)
97+
{
98+
sb.Append(name)
99+
.Append(':')
100+
.Append(value)
101+
.Append('\n');
102+
}
102103

103-
return ComputeHmacSHA256(appSecret, signature);
104+
var signSrcText = sb.ToString(0, sb.Length - 1); // Ignore the last '\n'
105+
return ComputeHmacSHA256(appSecret, signSrcText);
104106
}
105107

106108
protected override async Task<AuthenticationTicket> CreateTicketAsync(
@@ -110,14 +112,14 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync(
110112
{
111113
using var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
112114
request.Headers.Add("access-token", tokens.AccessToken);
113-
request.Headers.Add("x-bili-accesskeyid", Options.ClientId);
114-
request.Headers.Add("x-bili-content-md5", "d41d8cd98f00b204e9800998ecf8427e"); // It's a GET request so there's no content, so we send the MD5 hash of an empty string
115-
request.Headers.Add("x-bili-signature-method", "HMAC-SHA256");
116-
request.Headers.Add("x-bili-signature-nonce", Base64Url.EncodeToString(RandomNumberGenerator.GetBytes(256 / 8)));
117-
request.Headers.Add("x-bili-signature-version", "2.0");
118-
request.Headers.Add("x-bili-timestamp", TimeProvider.GetUtcNow().ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture));
119-
120-
var signature = BuildSignatureString(request, Options.ClientSecret);
115+
116+
var xbiliHeaders = BuildXBiliHeaders();
117+
foreach ((var name, var value) in xbiliHeaders)
118+
{
119+
request.Headers.Add(name, value);
120+
}
121+
122+
var signature = BuildSignatureString(xbiliHeaders, Options.ClientSecret);
121123
request.Headers.Add("Authorization", signature);
122124

123125
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
@@ -145,6 +147,23 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync(
145147
return new AuthenticationTicket(context.Principal!, context.Properties, Scheme.Name);
146148
}
147149

150+
private SortedList<string, string> BuildXBiliHeaders() => new(6, StringComparer.OrdinalIgnoreCase)
151+
{
152+
{ "x-bili-accesskeyid", Options.ClientId },
153+
{ "x-bili-content-md5", "d41d8cd98f00b204e9800998ecf8427e" }, // It's a GET request so there's no content, so we send the MD5 hash of an empty string
154+
{ "x-bili-signature-method", "HMAC-SHA256" },
155+
{ "x-bili-signature-nonce", GenerateNonce() },
156+
{ "x-bili-signature-version", "2.0" },
157+
{ "x-bili-timestamp", TimeProvider.GetUtcNow().ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture) }
158+
};
159+
160+
private static string GenerateNonce()
161+
{
162+
Span<byte> bytes = stackalloc byte[256 / 8];
163+
RandomNumberGenerator.Fill(bytes);
164+
return Base64Url.EncodeToString(bytes);
165+
}
166+
148167
/// <summary>
149168
/// Check the code sent back by server for potential server errors.
150169
/// </summary>
@@ -154,14 +173,13 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync(
154173
/// <returns>True if succeed, otherwise false.</returns>
155174
private static bool ValidateReturnCode(JsonElement element, out int code)
156175
{
157-
code = 0;
158176
if (!element.TryGetProperty("code", out JsonElement errorCodeElement))
159177
{
178+
code = 0;
160179
return true;
161180
}
162181

163182
code = errorCodeElement.GetInt32();
164-
165183
return code == 0;
166184
}
167185

test/AspNet.Security.OAuth.Providers.Tests/Bilibili/BilibiliTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* for more information concerning the license and the contributors participating to this project.
55
*/
66

7-
using System.Web;
87
using Microsoft.AspNetCore.WebUtilities;
98

109
namespace AspNet.Security.OAuth.Bilibili;

0 commit comments

Comments
 (0)