我有一个rest式的WCF服务,它托管在IIS中,通过SSL使用WebHttpBinding和json。
服务现在需要认证/授权。似乎有很多旧的信息可能仍然与实现身份验证的不同方法相关,也可能不相关,但它们似乎都不适用于这种特定的端点绑定混合。
我尝试过许多不同类型的绑定,使用基于SOAP的身份验证实现起来相对简单,但使用json就不是这样了。大多数信息都表明身份验证被IIS篡夺了,但我已经克服了这个障碍。
我想使用基本的身份验证,像这样(示例提琴请求):
GET https://secure.dev.myco.local/api/users/v1/0b7478c5-d25a-4039-9cf4-614b2ef4e04d HTTP/1.1
User-Agent: Fiddler
Host: secure.dev.myco.local
Authorization: Basic dGVzdHVzZXI6UEAkJHcwcmQh
我的业务行为配置如下:
<behavior name="SslBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<serviceCertificate findValue="CN=*.dev.myco.local" x509FindType="FindBySubjectDistinguishedName" storeLocation="LocalMachine" />
</serviceCredentials>
<serviceAuthenticationManager authenticationSchemes="Basic"
serviceAuthenticationManagerType="Test.Api.TestAuthenticationManager, Test.Api" />
</behavior>
绑定定义:<webHttpBinding>
<binding name="ProductBinding">
<security mode="Transport" />
</binding>
<binding name="UserBinding">
<security mode="Transport" />
</binding>
</webHttpBinding>
和服务定义:
<service name="Test.Api.ApiService" behaviorConfiguration="SslServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="https://secure.dev.myco.local/api/"/>
</baseAddresses>
</host>
<endpoint address="products/v1" name="ProductService" contract="Test.Api.Product.V1.IProductService" binding="webHttpBinding" bindingConfiguration="ProductBinding" behaviorConfiguration="ProductEndpointWebHttpBehavior" />
<endpoint address="users/v1" name="UserService" contract="Test.Api.User.V1.IUserService" binding="webHttpBinding" bindingConfiguration="UserBinding" behaviorConfiguration="UserEndpointWebHttpBehavior" />
</service>
我的用户帐户将保存在数据库中的一个表中,身份验证将凭据传递给身份验证提供者,如果用户进行身份验证,该提供者将返回IIdentity、IPrincipal或布尔值。然后根据需要将其附加到上下文。因此,我需要我的应用程序来处理身份验证,而不仅仅是将身份验证交给IIS。
我能够成功地访问来自HTTP报头的凭据,并通过从身份验证管理器派生并使用以下示例代码重写authenticate方法来对用户进行身份验证—生成IPrincipal并将其附加到我所期望的上下文中:
public class TestAuthenticationManager : ServiceAuthenticationManager
{
public override ReadOnlyCollection<IAuthorizationPolicy> Authenticate(ReadOnlyCollection<IAuthorizationPolicy> authPolicy, Uri listenUri, ref Message message)
{
var requestProperties =
(HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name];
var rawAuthHeader = requestProperties.Headers["Authorization"];
AuthenticationHeader authHeader = null;
if (AuthenticationHeader.TryDecode(rawAuthHeader, out authHeader)) ;
{
var identity = new GenericIdentity(authHeader.Username);
var principal = new GenericPrincipal(identity, new string[] {});
var httpContext = new HttpContextWrapper(HttpContext.Current)
{
User = principal,
};
if (httpContext.User != null)
return null;
}
SendUnauthorizedResponse();
return base.Authenticate(authPolicy, listenUri, ref message);
}
private void SendUnauthorizedResponse()
{
HttpContext.Current.Response.StatusCode = 401;
HttpContext.Current.Response.StatusDescription = "Unauthorized";
HttpContext.Current.Response.Headers.Add("WWW-Authenticate", "Basic realm="site"");
HttpContext.Current.Response.End();
}
}
在退出验证方法时,我的服务响应以下错误:
服务器在处理请求时遇到错误。一个例外消息是"调用者未通过服务进行身份验证。"看到有关服务器日志的详细信息。异常堆栈跟踪是:atSystem.ServiceModel.Dispatcher.AuthenticationBehavior.Authenticate (MessageRpc&rpc)System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11 (MessageRpc&System.ServiceModel.Dispatcher.MessageRpc.Process(布尔值isOperationContextSet)
如果我从配置中删除身份验证引用(如您所料),那么服务可以正常工作,但是用户是未经过身份验证的,因此,如果我向任何服务方法添加任何授权,它将无法处理该部分。
该方法似乎没有错误地退出。有人能解释一下为什么这个配置会出现这个异常吗?我一直在谷歌上搜索,直到奶牛回家,我发现的信息都与其他配置的有限数量的变化有关,但没有WebHttpBinding与Json托管在IIS中使用SSL。
if (httpContext.User != null) return null;
行是问题所在。如果Authenticate方法返回Null作为授权策略,Dispatcher将抛出异常。
这里的条件总是为真,所以它总是返回null。您需要返回一个授权策略,而不是null。