.NET7 Blazor WebAssembly托管公共表单:如何保障仅自有客户端访问API?
解决方案推荐
针对Blazor WebAssembly未认证场景下的API访问控制,结合你的需求,推荐以下几种实用方案:
1. API密钥+请求签名验证
这种方式通过签名确保请求来自合法客户端,同时防止重放攻击,适合对安全性要求中等的场景。
实现步骤:
客户端侧:
- 在项目中嵌入一个仅自有客户端知晓的API密钥(可通过编译时常量或混淆工具降低被反编译的风险)。
- 每次提交表单时,生成当前Unix时间戳。
- 将请求体内容、时间戳与API密钥拼接,使用HMAC-SHA256哈希算法生成签名。
- 将时间戳和签名放入自定义请求头,同时发送表单数据。
示例代码(Blazor WASM中使用
HttpClient):private async Task SubmitForm(FormData formData) { var apiKey = "YOUR_SECRET_API_KEY"; var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); var requestBody = JsonSerializer.Serialize(formData); // 生成签名 using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(apiKey)); var signatureBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes($"{requestBody}{timestamp}")); var signature = Convert.ToBase64String(signatureBytes); // 构造请求 var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/form") { Content = new StringContent(requestBody, Encoding.UTF8, "application/json") }; requestMessage.Headers.Add("X-Request-Timestamp", timestamp); requestMessage.Headers.Add("X-Request-Signature", signature); await _httpClient.SendAsync(requestMessage); }服务器侧:
- 创建自定义
ActionFilterAttribute或中间件,提取请求头中的时间戳和签名。 - 验证时间戳是否在有效窗口内(如5分钟),防止重放攻击。
- 使用相同的API密钥和算法重新计算签名,与请求头中的签名对比。
- 签名一致且时间戳有效则允许请求,否则返回403禁止访问。
示例代码(ASP.NET Core过滤器):
public class RequestSignatureValidationFilter : IAsyncActionFilter { private readonly IConfiguration _configuration; public RequestSignatureValidationFilter(IConfiguration configuration) { _configuration = configuration; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var apiKey = _configuration["ApiSettings:SecretKey"]; var request = context.HttpContext.Request; // 读取请求头 if (!request.Headers.TryGetValue("X-Request-Timestamp", out var timestampValues) || !request.Headers.TryGetValue("X-Request-Signature", out var signatureValues)) { context.Result = new ForbidResult(); return; } var timestamp = timestampValues.First(); var signature = signatureValues.First(); // 验证时间戳有效期 if (!long.TryParse(timestamp, out var unixTime) || DateTimeOffset.UtcNow.ToUnixTimeSeconds() - unixTime > 300) { context.Result = new ForbidResult(); return; } // 读取请求体 request.Body.Position = 0; var requestBody = await new StreamReader(request.Body).ReadToEndAsync(); request.Body.Position = 0; // 重新计算签名并对比 using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(apiKey)); var computedSignatureBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes($"{requestBody}{timestamp}")); var computedSignature = Convert.ToBase64String(computedSignatureBytes); if (!computedSignature.Equals(signature, StringComparison.Ordinal)) { context.Result = new ForbidResult(); return; } await next(); } }注册过滤器到
Program.cs:builder.Services.AddControllersWithViews(options => { options.Filters.Add<RequestSignatureValidationFilter>(); });- 创建自定义
2. 客户端证书验证
这种方式安全性更高,适合对数据安全要求严格的场景,通过客户端证书标识合法客户端。
实现步骤:
客户端侧:
- 生成客户端证书(可使用OpenSSL或Windows证书管理器),将
.pfx证书文件嵌入Blazor WASM项目,设置为“嵌入的资源”。 - 在Blazor WASM中加载证书,发起请求时携带证书。
示例代码:
private async Task SubmitForm(FormData formData) { // 加载嵌入的证书 var assembly = typeof(YourComponent).Assembly; using var certStream = assembly.GetManifestResourceStream("YourProject.Client.cert.pfx"); var certificate = new X509Certificate2(await certStream.ReadAllBytesAsync(), "CERT_PASSWORD"); // 创建带证书的HttpClientHandler var handler = new HttpClientHandler(); handler.ClientCertificates.Add(certificate); using var httpClient = new HttpClient(handler); await httpClient.PostAsJsonAsync("/api/form", formData); }- 生成客户端证书(可使用OpenSSL或Windows证书管理器),将
服务器侧:
- 在
Program.cs中配置Kestrel接受客户端证书:
builder.WebHost.ConfigureKestrel(options => { options.ConfigureHttpsDefaults(httpsOptions => { httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate; // 生产环境替换为具体证书验证逻辑,比如校验颁发机构或指纹 httpsOptions.AllowAnyClientCertificate(); }); });- 添加中间件验证证书合法性:
app.Use(async (context, next) => { var clientCert = context.Connection.ClientCertificate; if (clientCert == null || !IsValidClientCertificate(clientCert)) { context.Response.StatusCode = StatusCodes.Status403Forbidden; await context.Response.WriteAsync("Invalid client certificate"); return; } await next(); }); // 自定义证书验证逻辑 private bool IsValidClientCertificate(X509Certificate2 cert) { var validThumbprint = "YOUR_CERT_THUMBPRINT"; return cert.Thumbprint.Equals(validThumbprint, StringComparison.OrdinalIgnoreCase); }- 在
3. 改进防伪造令牌方案(辅助增强)
如果要继续使用防伪造令牌,可通过以下方式增强安全性:
- 将防伪造令牌的Cookie设置为
HttpOnly+SameSite=Strict,同时通过自定义请求头携带令牌(Blazor WASM可通过HttpClient拦截器自动添加)。 - 在服务器端,将防伪造令牌与客户端请求特征(如User-Agent)绑定,验证令牌时同时校验这些特征,降低令牌被盗用的风险。
内容的提问来源于stack exchange,提问作者Sébastien Foucher
相关产品推荐
相关产品推荐

