如何在.NET后端应用中授权验证Shopify Webhook请求?
Shopify Webhook 请求的.NET后端授权验证方案
你遇到的问题核心是:Shopify Webhook的身份验证机制和普通OAuth授权不同,不能直接用通用的[Authenticate]属性,得通过Shopify提供的HMAC签名来验证请求合法性。
解决步骤:
移除或替换通用认证属性
先把端点上的[Authenticate]去掉,因为这个属性对应的认证中间件无法识别Shopify Webhook的验证规则,会直接拦截请求。实现HMAC签名验证逻辑
Shopify会在每个Webhook请求的X-Shopify-Hmac-SHA256请求头里带上签名,你需要用自己应用的API Secret Key(在Shopify后台应用设置的API凭证里获取),对原始请求体计算HMAC-SHA256签名,再和请求头里的签名对比,一致则说明请求合法。
代码实现示例
方式一:自定义Action过滤器(推荐复用)
创建一个专门验证Shopify Webhook的过滤器:
using Microsoft.AspNetCore.Mvc.Filters; using System.Security.Cryptography; using System.Text; public class ValidateShopifyWebhookAttribute : ActionFilterAttribute { private readonly string _apiSecretKey; public ValidateShopifyWebhookAttribute(string apiSecretKey) { _apiSecretKey = apiSecretKey; } public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var request = context.HttpContext.Request; // 读取请求头中的HMAC签名 if (!request.Headers.TryGetValue("X-Shopify-Hmac-SHA256", out var hmacValue)) { context.Result = new Microsoft.AspNetCore.Mvc.UnauthorizedResult(); return; } // 读取原始请求体(必须用原始内容计算签名) request.Body.Position = 0; var requestBody = await new StreamReader(request.Body).ReadToEndAsync(); request.Body.Position = 0; // 重置流位置,方便后续处理请求体 // 计算请求体的HMAC签名 var secretBytes = Encoding.UTF8.GetBytes(_apiSecretKey); var bodyBytes = Encoding.UTF8.GetBytes(requestBody); using var hmac = new HMACSHA256(secretBytes); var computedHmac = Convert.ToBase64String(hmac.ComputeHash(bodyBytes)); // 对比签名是否一致 if (!computedHmac.Equals(hmacValue, StringComparison.Ordinal)) { context.Result = new Microsoft.AspNetCore.Mvc.UnauthorizedResult(); return; } // 验证通过,继续执行端点逻辑 await next(); } }
然后在Program.cs中注册过滤器(.NET 6+):
builder.Services.AddScoped<ValidateShopifyWebhookAttribute>(sp => new ValidateShopifyWebhookAttribute(builder.Configuration["Shopify:ApiSecretKey"]));
最后在Webhook端点上使用:
[HttpPost("shopify-webhook-endpoint")] [ServiceFilter(typeof(ValidateShopifyWebhookAttribute))] public IActionResult HandleShopifyWebhook() { // 这里写你的Webhook处理逻辑 return Ok(); }
方式二:端点内直接验证(快速实现)
如果只是单个端点需要验证,也可以直接在方法内写验证逻辑:
[HttpPost("shopify-webhook-endpoint")] public async Task<IActionResult> HandleShopifyWebhook(IConfiguration config) { var request = HttpContext.Request; var hmacHeader = request.Headers["X-Shopify-Hmac-SHA256"].FirstOrDefault(); if (string.IsNullOrEmpty(hmacHeader)) { return Unauthorized(); } // 读取原始请求体 request.Body.Position = 0; var requestBody = await new StreamReader(request.Body).ReadToEndAsync(); request.Body.Position = 0; // 计算并对比签名 var secretKey = config["Shopify:ApiSecretKey"]; var secretBytes = Encoding.UTF8.GetBytes(secretKey); var bodyBytes = Encoding.UTF8.GetBytes(requestBody); using var hmac = new HMACSHA256(secretBytes); var computedHmac = Convert.ToBase64String(hmac.ComputeHash(bodyBytes)); if (!computedHmac.Equals(hmacHeader, StringComparison.Ordinal)) { return Unauthorized(); } // 处理Webhook业务逻辑 return Ok(); }
注意事项
- 一定要用原始请求体计算签名,不能对请求体做任何修改(比如解析后再序列化),否则签名会不匹配。
- API Secret Key要妥善保管,不要硬编码在代码里,用配置文件或环境变量存储。
- 除了HMAC验证,也可以结合
X-Shopify-Shop-Domain头验证请求来源的店铺是否是你授权过的店铺,进一步提升安全性。
内容的提问来源于stack exchange,提问作者Devon K
相关产品推荐
相关产品推荐

