Como você implementaria a chave da API no serviço de dados WCF?
Pergunta
Existe uma maneira de exigir uma chave da API no URL / ou em alguma outra maneira de passar ao serviço uma chave privada para conceder acesso aos dados?
Eu tenho isso agora ...
using System;
using System.Data.Services;
using System.Data.Services.Common;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Web;
using Numina.Framework;
using System.Web;
using System.Configuration;
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class odata : DataService {
public static void InitializeService(DataServiceConfiguration config) {
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
//config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
protected override void OnStartProcessingRequest(ProcessRequestArgs args) {
HttpRequest Request = HttpContext.Current.Request;
if(Request["apikey"] != ConfigurationManager.AppSettings["ApiKey"])
throw new DataServiceException("ApiKey needed");
base.OnStartProcessingRequest(args);
}
}
... Isso funciona, mas não é perfeito, porque você não pode chegar aos metadados e descobrir o serviço através do Explorer de Referência de Serviço Add Service. Eu poderia verificar se $ metadados está no URL, mas parece um hack. Existe uma maneira melhor?
Solução
Eu sugeriria o uso do cabeçalho da autorização para passar o apikey em vez de passá -lo na sequência de consultas. É para isso que existe e ajuda a manter as teclas da API fora dos arquivos de log.
Eu não acho que há algo realmente errado em verificar a presença de '$ metadados' no URL. Você está escrevendo o código do servidor e o servidor possui o espaço URI, portanto, tomar decisões com base no texto na URL da solicitação é o que é o servidor. Você poderia usar algo como,
if (requestUrl.Segments.Last().Replace('/','') != '$metadata')
Em vez de pesquisar toda a corda URI, se ela se sentir menos nojenta!
Outras dicas
Parece a técnica apresentada em esse vídeo Funciona bem mesmo em serviços de dados da WCF. Você cria uma subclasse personalizada de ServiceAuthorizationManager
(Vejo Msdn), sobrepor CheckAccessCore()
, e registre -o em web.config.
Consegui funcionar passando uma chave em um cabeçalho HTTP da solicitação. o OperationContext
passado para CheckAccessCore
não lhe dá uma maneira de pegar os cabeçalhos de solicitação HTTP, mas você pode obtê -los via HttpContext.Current.Request.Headers
. Você pode obter o cabeçalho adequado dessa coleção e verificá -lo como precisar.
Aqui está o registro necessário em web.config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceAuthorization serviceAuthorizationManagerType="FullyQualifiedTypeNameHere, ProjectNameHere" />
</behavior>
</serviceBehaviors>
</behaviors>
ATUALIZAR: Eu estava errado em poder tirar cabeçalhos de HttpContext.Current.Request.Headers
; HttpContext.Current
é nulo ao executar no IIS (mas curiosamente não quando depurar). Em vez disso, use WebOperationContext.Current.IncomingRequest.Headers
conforme essa questão.
Atualização 2: HttpContext.Current
é apenas nulo quando você não está executando o WCF no modo de compatibilidade do ASP.NET. Você pode ativar isso adicionando a seguinte linha ao web.config no nível do aplicativo no system.serviceModel
nó:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
Adicione também isso acima da implementação do seu serviço, se você tiver um serviço WCF de baunilha em execução, além do serviço ADO.NET:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
Então você pode conseguir HttpContext.Current.Request.Headers
e todas as outras coisas fornecidas pelo HttpRequest
classe.
Você pode verificar o tipo de solicitação e deixar as chamadas WSDL passarem com a tecla API.
Não tenho certeza de quais são seus objetivos da API, mas você pode usar um certificado de cliente.