You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET后端应用中授权验证Shopify Webhook请求?

Shopify Webhook 请求的.NET后端授权验证方案

你遇到的问题核心是:Shopify Webhook的身份验证机制和普通OAuth授权不同,不能直接用通用的[Authenticate]属性,得通过Shopify提供的HMAC签名来验证请求合法性。

解决步骤:

  1. 移除或替换通用认证属性
    先把端点上的[Authenticate]去掉,因为这个属性对应的认证中间件无法识别Shopify Webhook的验证规则,会直接拦截请求。

  2. 实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 13:33:05