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

12
我有一个托管在 IIS 上的网站,使用 Windows 认证并公开 WCF Web 服务。
我使用终结点行为配置此服务:
<serviceAuthorization principalPermissionMode ="UseAspNetRoles" 
                   roleProviderName="MyRoleProvider"/>

一个装订:
 <security mode="TransportCredentialOnly">
    <transport clientCredentialType="Ntlm" />
 </security>

当该服务被调用时,Thread.CurrentPrincipal 会被设置为一个 RolePrincipal,其中包含客户端 Windows 身份和由配置的提供程序提供的角色。
世界一切正常。
现在,我添加了一些额外的 WCF 服务,这些服务通过 REST-ful Ajax 调用进行消费: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服务一样?
更新:这里 有个博客,作者也遇到了同样的问题,并通过实现自定义行为来解决它。肯定有一种方法可以直接解决这个问题吧?
更新2:我回来给这个问题添加一个赏金,因为在一个新项目中,我正在使用.NET 3.5上的WCF WebGet启用的服务。
我尝试了许多选项,包括设置principalPermissionMode =“None”,但没有任何作用。以下是发生的情况:
我导航到调用我的服务的WebGet URL: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。烦死了!
我看到建议实现自定义IAuthorizationPolicy,如果找不到更好的解决方案,我会研究一下。但是为什么我需要实现自定义策略才能使用现有的ASP.NET授权功能呢?如果我的principalPermissionMode =“UseAspNetRoles”,那么WCF应该知道我想要什么吧?

你遇到过这个问题吗?(从模糊的描述中我无法确定它是一个更复杂还是更简单的解决方案。)http://social.msdn.microsoft.com/Forums/en/wcf/thread/70986777-a3bd-48ad-8196-090d0e897dc6 - Kevin Stricker
@mootinator,不是同一个情况。我使用了RoleProvider,并且HttpContext.Current.User被正确设置了,但Thread.CurrentPrincipal没有。 - Joe
我想知道这是否是.NET 4.5或4.5.1中已经修复的错误。你尝试过针对最新的.NET版本进行开发吗? - noseratio - open to work
@Noseratio - 很好的观点,但我在4.5版本中也遇到了同样的问题。 - Joe
2个回答

6

这是一个有趣的问题。我没有和你一样的设置,所以很难测试我的建议是否完全适用于你的使用情况,但是我可以分享我们在类似项目中的经验。

如何保持Thread.CurrentPrincipalHttpContext.Current.User同步

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

然后,我们附加到HttpApplication.AuthenticateRequest事件,该事件发生在请求生命周期非常早的阶段。

在我们的AuthenticateRequest事件处理程序中,我们实现了我们的应用程序特定要求,包括设置Thread.CurrentPrincipal,如果必要,还设置当前上下文用户。通过这种方式,您只需为整个应用程序实现此代码一次,如果更改(例如,如果您实现了自定义主体标识),则只需更改一个地方即可。(不要在每个服务方法中复制此代码。)

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;
    }
}

我们的情况稍微复杂一些,因为我们实现了自定义的,创建了一个实例,然后将其分配给和。但是,上面的内容就是你所要求的。
不要忘记在中注册你的新HttpModule!
对于集成应用程序池:
<system.webServer>
    <modules>
      <add name="AuthenticationModule" type="YourNameSpace.AuthenticationModule" preCondition="integratedMode" />
    </modules>
</system.webServer>

对于旧的经典应用程序池,您需要将其放置在 <system.web><httpModules></httpModules></system.web> 中。

您可能需要尝试使用AuthenticationRequest事件处理程序内部的内容和/或注册处理程序的顺序。因为我们的处理程序是完全自定义的,所以它可能与您需要的不同。我们实际上获取Forms身份验证cookie,解密它等等... 您可能需要调用一些内置的WindowsAuthentication方法。

我认为这是处理应用程序身份验证问题的更通用的方法,因为它适用于所有 HttpRequest ,无论是页面请求, IHttpHandler ,第三方组件等等...... 这将使您的应用程序保持一致。


谢谢您的回复。我知道通过编程有办法实现我想要的,比如在我提出问题中链接的博客中描述的自定义行为。但我真正寻找的是仅通过配置实现这一点的方法,或者一个权威来源解释为什么这不可能。 - Joe

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接