1

SSL 経由の基本認証を使用して要求が認証される WCF REST サービスを開発しています。ただし、認証チャレンジを送信する前に、事前共有 API キーを使用してリクエストが有効であることを確認したいと考えています。URL でキー値を渡したくないので、カスタム HTTP ヘッダーが最適なソリューションですか? X-APIKey: キー値のようなもの。

HttpModule でユーザーの資格情報を認証しています。

public void OnAuthenticateRequest(object source, EventArgs eventArgs)
    {
        HttpApplication app = (HttpApplication)source;

        if (!app.Request.IsSecureConnection)
        {
            app.Response.StatusCode = 403;
            app.Response.StatusDescription = "SSL Required";
            app.Response.End();
            return;
        }

        string authHeader = app.Request.Headers[AUTH_HEADER];
        if (authHeader == null)
        {
            app.Response.StatusCode = 401;
            app.Response.End();
            return;
        }

        ClientCredentials credentials = ClientCredentials.FromHeader(authHeader);
        if (credentials.Authenticate())
        {
            app.Context.User = new GenericPrincipal(new GenericIdentity(credentials.Id), null);
        }
        else
        {
            DenyAccess(app);
        }
    }
4

1 に答える 1

1

これは良い代替手段です(ヘッダーで渡します)。次に、カスタムメッセージインスペクターを使用して、以下のコードに示すように、特定のエンドポイントに対するすべてのリクエストに共有キーが存在することを検証できます。

public class StackOverflow_13463251
{
    const string SharedKeyHeaderName = "X-API-Key";
    const string SharedKey = "ThisIsMySharedKey";
    [ServiceContract]
    public interface ITest
    {
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        string Echo(string text);
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        int Add(int x, int y);
    }
    public class Service : ITest
    {
        public string Echo(string text)
        {
            return text;
        }
        public int Add(int x, int y)
        {
            return x + y;
        }
    }
    public class ValidateSharedKeyInspector : IEndpointBehavior, IDispatchMessageInspector
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            HttpRequestMessageProperty httpReq = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
            string apiKey = httpReq.Headers[SharedKeyHeaderName];
            if (!SharedKey.Equals(apiKey))
            {
                throw new WebFaultException<string>("Missing api key", HttpStatusCode.Unauthorized);
            }

            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }
    }
    static void SendRequest(string uri, bool includeKey)
    {
        string responseBody = null;
        Console.WriteLine("Request to {0}, {1}", uri, includeKey ? "including shared key" : "without shared key");

        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
        req.Method = "GET";
        if (includeKey)
        {
            req.Headers[SharedKeyHeaderName] = SharedKey;
        }

        HttpWebResponse resp;
        try
        {
            resp = (HttpWebResponse)req.GetResponse();
        }
        catch (WebException e)
        {
            resp = (HttpWebResponse)e.Response;
        }

        Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
        foreach (string headerName in resp.Headers.AllKeys)
        {
            Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
        }

        Console.WriteLine();
        Stream respStream = resp.GetResponseStream();
        responseBody = new StreamReader(respStream).ReadToEnd();
        Console.WriteLine(responseBody);

        Console.WriteLine();
        Console.WriteLine("  *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*  ");
        Console.WriteLine();
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), "");
        endpoint.Behaviors.Add(new WebHttpBehavior());
        endpoint.Behaviors.Add(new ValidateSharedKeyInspector());
        host.Open();
        Console.WriteLine("Host opened");

        SendRequest(baseAddress + "/Echo?text=Hello+world", false);
        SendRequest(baseAddress + "/Echo?text=Hello+world", true);
        SendRequest(baseAddress + "/Add?x=6&y=8", false);
        SendRequest(baseAddress + "/Add?x=6&y=8", true);

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
于 2012-11-19T22:30:06.090 に答える