简体   繁体   English

如何使 ASPNETCORE 中缓存的 AWS API Gateway 端点无效?

[英]How do you invalidate a cached AWS API Gateway endpoint in ASPNETCORE?

[Note: I've already worked out an answer to this but struggled to find anything online so I'm adding it here] [注意:我已经找到了答案,但很难在网上找到任何东西,所以我在这里添加]

I need to invalidate the cache for an individual AWS API Gateway endpoint using ASPNETCORE.我需要使用 ASPNETCORE 使单个 AWS API Gateway 端点的缓存无效。

The docs say to send a signed request.文档说要发送一个签名的请求。 How do you do this in .NET?你如何在 .NET 中做到这一点?

I'm answering my own question as I couldn't find much information online and it took a bit of time to get working.我正在回答我自己的问题,因为我在网上找不到太多信息,而且花了一些时间才开始工作。 Hopefully it'll help someone.希望它会帮助某人。

I've added code here: https://gist.github.com/secretorange/905b4811300d7c96c71fa9c6d115ee24我在这里添加了代码: https : //gist.github.com/secretorange/905b4811300d7c96c71fa9c6d115ee24

CacheInvalidationRequestBuilder.cs CacheInvalidationRequestBuilder.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;

namespace Aws
{
    public static class CacheInvalidationRequestBuilder
    {
        private const string ServiceName    = "execute-api";
        private const string Algorithm      = "AWS4-HMAC-SHA256";
        private const string ContentType    = "application/json";
        private const string DateTimeFormat = "yyyyMMddTHHmmssZ";
        private const string DateFormat     = "yyyyMMdd";

        public static WebRequest Build(CacheInvalidationRequestModel request)
        {
            string hashedRequestPayload = CreateRequestPayload(String.Empty);

            string authorization = Sign(request, hashedRequestPayload, "GET", request.AbsolutePath, request.QueryString);
            string requestDate = DateTime.UtcNow.ToString(DateTimeFormat);

            var webRequest = WebRequest.Create($"https://{request.Host}{request.AbsolutePath}");

            webRequest.Method = "GET";
            webRequest.ContentType = ContentType;

            webRequest.Headers.Add("Cache-Control", "max-age=0");
            webRequest.Headers.Add("Host", request.Host);
            webRequest.Headers.Add("X-Amz-Date", requestDate);

            webRequest.Headers.Add("Authorization", authorization);

            return webRequest;
        }

        private static string CreateRequestPayload(string jsonString)
        {
            return HexEncode(Hash(ToBytes(jsonString)));
        }

        private static string Sign(CacheInvalidationRequestModel request, string hashedRequestPayload, string requestMethod, string canonicalUri, string canonicalQueryString)
        {
            var currentDateTime = DateTime.UtcNow;

            var dateStamp = currentDateTime.ToString(DateFormat);
            var requestDate = currentDateTime.ToString(DateTimeFormat);
            var credentialScope = $"{dateStamp}/{request.Region}/{ServiceName}/aws4_request";

            var headers = new SortedDictionary<string, string> {
                { "cache-control", "max-age=0" },
                { "content-type", ContentType },
                { "host", request.Host },
                { "x-amz-date", requestDate }
            };

            var canonicalHeaders = string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n";

            // Task 1: Create a Canonical Request For Signature Version 4
            var SignedHeaders = String.Join(';', headers.Select(x => x.Key.ToLowerInvariant()));

            var canonicalRequest = $"{requestMethod}\n{canonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{SignedHeaders}\n{hashedRequestPayload}";

            var hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest)));

            // Task 2: Create a String to Sign for Signature Version 4
            var stringToSign = $"{Algorithm}\n{requestDate}\n{credentialScope}\n{hashedCanonicalRequest}";

            // Task 3: Calculate the AWS Signature Version 4
            var signingKey = GetSignatureKey(request.SecretKey, dateStamp, request.Region, ServiceName);
            var signature = HexEncode(HmacSha256(stringToSign, signingKey));

            // Task 4: Prepare a signed request
            // Authorization: algorithm Credential=access key ID/credential scope, SignedHeadaers=SignedHeaders, Signature=signature
            var authorization = $"{Algorithm} Credential={request.AccessKey}/{dateStamp}/{request.Region}/{ServiceName}/aws4_request, SignedHeaders={SignedHeaders}, Signature={signature}";

            return authorization;
        }

        private static byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName)
        {
            var kDate = HmacSha256(dateStamp, ToBytes("AWS4" + key));
            var kRegion = HmacSha256(regionName, kDate);
            var kService = HmacSha256(serviceName, kRegion);
            return HmacSha256("aws4_request", kService);
        }

        private static byte[] ToBytes(string str)
        {
            return Encoding.UTF8.GetBytes(str.ToCharArray());
        }

        private static string HexEncode(byte[] bytes)
        {
            return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant();
        }

        private static byte[] Hash(byte[] bytes)
        {
            return SHA256.Create().ComputeHash(bytes);
        }

        private static byte[] HmacSha256(string data, byte[] key)
        {
            return new HMACSHA256(key).ComputeHash(ToBytes(data));
        }
    }
}

CacheInvalidationRequestModel.cs CacheInvalidationRequestModel.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace Aws
{
    public class CacheInvalidationRequestModel
    {
        public string Region { get; set; }

        public string Host { get; set; }

        public string AbsolutePath { get; set; }

        public string QueryString { get; set; }

        public string AccessKey { get; set; }

        public string SecretKey { get; set; }
    }
}

How to use the code如何使用代码

To make a request, use code similar to:要发出请求,请使用类似于以下内容的代码:

var url = $"/myendpoint";

var model = GetCacheInvalidationRequestModel(url);

var request = CacheInvalidationRequestBuilder.Build(model);

try
{
  // Hit the endpoint
  using (var response = request.GetResponse())
  {
    // Not currently doing anything with the response
  }
}
catch(Exception ex)
{
  Logger.LogError(ex, "Problem invalidating cache for url: " + url);
}

The GetCacheInvalidationRequestModel method might look something like this (I pass in the model properties as IOptions ): GetCacheInvalidationRequestModel方法可能看起来像这样(我将模型属性作为IOptions ):

private CacheInvalidationRequestModel GetCacheInvalidationRequestModel(string absolutePath)
{
    return new CacheInvalidationRequestModel()
    {
        Region = Options.Region,
        Host = Options.Host,
        AccessKey = Options.InvalidatorKey,
        SecretKey = Options.InvalidatorSecret,
        AbsolutePath = absolutePath
    };
}

AWS Information AWS 信息

AWS docs for building signed requests are here: https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html用于构建签名请求的 AWS 文档在这里: https : //docs.aws.amazon.com/general/latest/gr/sigv4_signing.html

Your AWS user will need an attached policy, as shown here: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html您的 AWS 用户将需要附加策略,如下所示: https : //docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "execute-api:InvalidateCache"
      ],
      "Resource": [
        "arn:aws:execute-api:region:account-id:api-id/stage-name/GET/resource-path-specifier"
      ]
    }
  ]
}

NOTE: You can use wildcards if you like.注意:您可以根据需要使用通配符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM