如何使用自托管 WCF OData 服务进行身份验证
How to use authentication with self hosted WCF OData service
我有一个在控制台应用程序中运行的 WCF 数据服务 DataServiceHost
我可以成功启动我的主机并使用此代码查询我的 WCF 数据服务
public void Start()
{
var uri = new Uri("http://localhost:12345/Products");
var host = new DataServideHost(typeof(ProductsDataService), uri);
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
host.Description.Behaviors.Add(new ServiceMetadataBehavior());
if (host.Description.Behaviors.Find<ServiceDebugBehavior>() == null)
host.Description.Behaviors.Add(new ServiceDebugBehavior());
host.Description.Behaviors.Find<ServiceMetadataBehavior()
.HttpGetEnabled = true;
host.Description.Behaviors.Find<ServiceDebugBehavior>()
.IncludeExceptionDetailInFaults = true;
host.AddServiceEndpoint(
new ServiceEndpoint(ContractDescription.GetContract(serviceType))
{
Name = "default",
Address = new EndpointAddress(baseAddress),
Contract = ContractDescription.GetContract(
typeof(IRequestHandler)),
Binding = new WebHttpBinding(),
});
host.Open();
}
我想如何通过基本身份验证或其他方式保护此服务(请注意,我的服务将通过 https 保护)
我找到了很多关于如何使用 IHttpModule
通过 IIS 保护 DataService 的示例,但是我还发现 post 说我不能将 HttpModules 与我的 DataServiceHost 一起使用。
有人可以给我一些关于如何实施身份验证的提示吗?
基于博客 post OData and Authentication – Part 4 – Server Side Hooks 我创建了一个用于 DataServices 的扩展方法。请记住,您只能通过 HTTPS 使用基本身份验证,否则您的密码将被其他人看到,因为用户名/密码只是 base64 解码。出于测试目的,您可以删除此检查 if (!context.Request.IsSecureConnection) return false;
用法:
public ProductsDataService : EntityFrameworkDataService<ProductsContext>
{
private static validator = new UserValidator();
public ProductsDataService()
{
this.UseBasicAuthentification("My Realm", validator);
}
private class UserValidator : IUserValidator
{
public IPrincipal Validate(string username, string password)
{
// just an example implementation
if (!"1234".Equals(password)) retur null;
return new GenericPrincipal(
new GenericIdentity(username), "Admin", "User");
}
}
}
这是实现。您只需创建一个满足您需求的 IUserValidator 实现。
public static class DataServiceExtensions
{
public static void UseBasicAuthentification<T>(
this DataService<T> service, string realm, IUserValidator validator)
{
service.ProcessingPipeline.ProcessingRequest += (_sender, _e) =>
{
if (!Authenticate(_e.OperationContext, validator))
{
_e.OperationContext.ResponseHeaders.Add(
"WWW-Authenticate", "Basic realm=" +
Convert.ToBase64String(
Encoding.UTF8.GetBytes(GlobalConfiguration.Realm)));
throw new DataServiceException(401, "401 Unauthorized");
}
};
}
static bool Authenticate(DataServiceOperationContext context,
IUserValidator validator)
{
if (!context.RequestHeaders.AllKeys.Contains("Authorization"))
return false;
// Remember claims based security should be only be
// used over HTTPS
if (!context.Request.IsSecureConnection)
return false;
string authHeader = context.RequestHeaders["Authorization"];
IPrincipal principal = null;
if (TryGetPrincipal(authHeader, validator, out principal))
{
//context.User = principal;
return true;
}
return false;
}
private static bool TryGetPrincipal(string authHeader,
IUserValidator validator, out IPrincipal principal)
{
var protocolParts = authHeader.Split(' ');
if (protocolParts.Length != 2)
{
principal = null;
return false;
}
else if (protocolParts[0] == "Basic")
{
var parameter = Encoding.UTF8.GetString(
Convert.FromBase64String(protocolParts[1]));
var parts = parameter.Split(':');
if (parts.Length != 2)
{
principal = null;
return false;
}
var username = parts[0];
var password = parts[1];
principal = validator.Validate(username, password);
return principal != null;
}
else
{
principal = null;
return false;
}
}
}
public interface IUserValidator
{
IPrincipal Validate(string username, string password);
}
我有一个在控制台应用程序中运行的 WCF 数据服务 DataServiceHost
我可以成功启动我的主机并使用此代码查询我的 WCF 数据服务
public void Start()
{
var uri = new Uri("http://localhost:12345/Products");
var host = new DataServideHost(typeof(ProductsDataService), uri);
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
host.Description.Behaviors.Add(new ServiceMetadataBehavior());
if (host.Description.Behaviors.Find<ServiceDebugBehavior>() == null)
host.Description.Behaviors.Add(new ServiceDebugBehavior());
host.Description.Behaviors.Find<ServiceMetadataBehavior()
.HttpGetEnabled = true;
host.Description.Behaviors.Find<ServiceDebugBehavior>()
.IncludeExceptionDetailInFaults = true;
host.AddServiceEndpoint(
new ServiceEndpoint(ContractDescription.GetContract(serviceType))
{
Name = "default",
Address = new EndpointAddress(baseAddress),
Contract = ContractDescription.GetContract(
typeof(IRequestHandler)),
Binding = new WebHttpBinding(),
});
host.Open();
}
我想如何通过基本身份验证或其他方式保护此服务(请注意,我的服务将通过 https 保护)
我找到了很多关于如何使用 IHttpModule
通过 IIS 保护 DataService 的示例,但是我还发现 post 说我不能将 HttpModules 与我的 DataServiceHost 一起使用。
有人可以给我一些关于如何实施身份验证的提示吗?
基于博客 post OData and Authentication – Part 4 – Server Side Hooks 我创建了一个用于 DataServices 的扩展方法。请记住,您只能通过 HTTPS 使用基本身份验证,否则您的密码将被其他人看到,因为用户名/密码只是 base64 解码。出于测试目的,您可以删除此检查 if (!context.Request.IsSecureConnection) return false;
用法:
public ProductsDataService : EntityFrameworkDataService<ProductsContext>
{
private static validator = new UserValidator();
public ProductsDataService()
{
this.UseBasicAuthentification("My Realm", validator);
}
private class UserValidator : IUserValidator
{
public IPrincipal Validate(string username, string password)
{
// just an example implementation
if (!"1234".Equals(password)) retur null;
return new GenericPrincipal(
new GenericIdentity(username), "Admin", "User");
}
}
}
这是实现。您只需创建一个满足您需求的 IUserValidator 实现。
public static class DataServiceExtensions
{
public static void UseBasicAuthentification<T>(
this DataService<T> service, string realm, IUserValidator validator)
{
service.ProcessingPipeline.ProcessingRequest += (_sender, _e) =>
{
if (!Authenticate(_e.OperationContext, validator))
{
_e.OperationContext.ResponseHeaders.Add(
"WWW-Authenticate", "Basic realm=" +
Convert.ToBase64String(
Encoding.UTF8.GetBytes(GlobalConfiguration.Realm)));
throw new DataServiceException(401, "401 Unauthorized");
}
};
}
static bool Authenticate(DataServiceOperationContext context,
IUserValidator validator)
{
if (!context.RequestHeaders.AllKeys.Contains("Authorization"))
return false;
// Remember claims based security should be only be
// used over HTTPS
if (!context.Request.IsSecureConnection)
return false;
string authHeader = context.RequestHeaders["Authorization"];
IPrincipal principal = null;
if (TryGetPrincipal(authHeader, validator, out principal))
{
//context.User = principal;
return true;
}
return false;
}
private static bool TryGetPrincipal(string authHeader,
IUserValidator validator, out IPrincipal principal)
{
var protocolParts = authHeader.Split(' ');
if (protocolParts.Length != 2)
{
principal = null;
return false;
}
else if (protocolParts[0] == "Basic")
{
var parameter = Encoding.UTF8.GetString(
Convert.FromBase64String(protocolParts[1]));
var parts = parameter.Split(':');
if (parts.Length != 2)
{
principal = null;
return false;
}
var username = parts[0];
var password = parts[1];
principal = validator.Validate(username, password);
return principal != null;
}
else
{
principal = null;
return false;
}
}
}
public interface IUserValidator
{
IPrincipal Validate(string username, string password);
}