在使用WebGet调用的WCF服务中未设置Thread.CurrentPrincipal

时间:2012-01-27 07:21:17

标签: asp.net .net ajax wcf

我在IIS中托管了一个使用Windows身份验证并公开WCF Web服务的网站。

我使用端点行为配置此服务:

<serviceAuthorization principalPermissionMode ="UseAspNetRoles" 
                   roleProviderName="MyRoleProvider"/>

和绑定:

 <security mode="TransportCredentialOnly">
    <transport clientCredentialType="Ntlm" />
 </security>

调用服务时,Thread.CurrentPrincipal设置为RolePrincipal,其中包含客户端的Windows身份和角色,由配置的提供商提供。

一切都与世隔绝。

现在我添加了REST-ful Ajax调用所使用的一些其他WCF服务:svc文件中的Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory",服务合同中的WebGet属性以及AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)关于服务实现的属性。

我还按照MSDN中的建议将以下咒语添加到web.config中:

<system.serviceModel>
    ...
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    ...
</system.serviceModel>

我的Ajax服务几乎以我想要的方式工作。当它被调用时,HttpContext.Current.User被设置为具有我期望的角色的RolePrincipal。但Thread.CurrentPrincipal仍设置为未经身份验证的GenericPrincipal

所以我需要为每个服务方法添加一行代码:

Thread.CurrentPrincipal = HttpContext.Current.User

我可以使用配置文件中的任何咒语来自动设置Thread.CurrentPrincipal,就像普通的SOAP服务一样吗?

更新 Here's来自有同样问题的人的博客,并通过实施自定义行为解决了这个问题。当然有办法开箱即用吗?

更新2 回来为此添加一个赏金,因为它在一个新项目中再次使我烦恼,在.NET 3.5上使用支持WCF WebGet的服务。

我已经尝试了很多选项,包括设置principalPermissionMode =“None”,但没有任何效果。这是发生的事情:

  • 我导航到调用我的服务的WebGet网址:http://myserver/MyService.svc/...

  • 我在Global.asax“Application_AuthorizeRequest”中放了一个断点。当命中此断点时,“HttpContext.Current.User”和“Thread.CurrentPrincipal”都已设置为使用我配置的ASP.NET RoleProvider的“RolePrincipal”。这是我想要的行为。

  • 当调用我的服务的OperationContract方法时,我有第二个断点。当遇到此断点时,HttpContext.Current.User仍然引用我的RolePrincipal,但Thread.CurrentPrincipal已更改为GenericPrincipal。 Aaargh。

我已经看到implement a custom IAuthorizationPolicy的建议,如果我找不到更好的解决方案,我会调查一下,但为什么我需要实现自定义策略来利用现有的ASP.NET授权功能?如果我有principalPermissionMode =“UseAspNetRoles”,肯定WCF应该知道我想要什么?

2 个答案:

答案 0 :(得分:6)

这是一个有趣的问题。我没有与您相同的设置,因此很难测试我的建议是否完全适用于您的用例,但我可以与类似项目分享对我们有用的内容。

我们如何将Thread.CurrentPrincipalHttpContext.Current.User保持在同步

我们编写了一个名为“AuthenticationModule”的HttpModule,它继承自IHtppModule

然后我们附加到请求生命周期中很早发生的HttpApplication.AuthenticateRequest事件。

在我们的AuthenticateRequest事件处理程序中,我们实现了特定于应用程序的要求,包括设置Thread.CurrentPrincipal,如果需要,还设置当前上下文用户。这样,您只需为整个应用程序实现一次此代码,如果它发生更改(如果您实现了自定义Principal IIDentity),则只有一个地方可以更改它。 (不要在每个服务方法中复制此代码。)

public class AuthenticationModule : IHttpModule
{
    public void Dispose() { return; }

    public void Init(HttpApplication app)
    {
        app.AuthenticateRequest += new EventHandler(app_AuthenticateRequest);
    }

    void app_AuthenticateRequest(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;

        // This is what you were asking for, but hey you 
        // could change this behavior easily.
        Thread.CurrentPrincipal = app.Context.User;
    }
}

我们实施自定义IIdentity时实际上有点复杂,创建GenericPrincipal的实例,然后将其分配给app.Context.UserThread.CurrentPrincipal;但是,以上就是你所要求的。

不要忘记在web.config中注册新的HttpModule!

对于集成的应用程序池:

<system.webServer>
    <modules>
      <add name="AuthenticationModule" type="YourNameSpace.AuthenticationModule" preCondition="integratedMode" />
    </modules>
</system.webServer>

对于旧的经典应用程序池,您必须将其放在<system.web><httpModules></httpModules></system.web>

您可能需要使用AuthenticationRequest事件处理程序内部的内容和/或注册处理程序的顺序。因为我们的完全是定制的,它可能与您需要的不同。我们实际上抓取了Forms Authentication cookie,解密它等等......你可能需要ping一些内置的WindowsAuthentication方法。

我认为这是处理应用程序身份验证内容的一种更通用的方式,因为它适用于所有HttpRequests,无论是页面请求,IHttpHandler,某些第三方组件等等......这将使整个应用程序保持一致。

答案 1 :(得分:1)

我不确定。也许这会有所帮助

<configuration>
  <system.web>
    <identity impersonate="true" />
  </system.web>
</configuration>

http://msdn.microsoft.com/en-us/library/134ec8tc(v=vs.80).aspx