我正在开发一个WCF REST服务,其中使用基于SSL的基本身份验证对请求进行身份验证。但是,在发送身份验证质询之前,我希望使用预共享API密钥确保请求有效。我不希望URL中传递的键值因此自定义HTTP头是最佳解决方案吗?像X-APIKey:keyvalue。
我在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);
}
}
答案 0 :(得分: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();
}
}