0

我正在使用 ASP.NET Ajax 从 ASP.NET 页面调用 WCF。我正在使用表单身份验证来保护网站。

在开发过程中一切都按预期工作,直到它被部署到生产服务器上,然后我开始收到 JavaScript 错误,因为找不到该服务。生产服务器使用 SSL,所以我在web.config中添加了以下内容:

<webHttpBinding>
    <binding name="webBinding">
    <security mode="Transport" />
    </binding>
</webHttpBinding>

这阻止了 JavaScript 错误的发生,但现在 WCF 服务的行为不像以前那样。

在将安全设置为传输之前,从 ASP.NET Ajax 调用 WCF 服务将Application_AuthenticateRequest在我的 Global.asax 中执行。这将基于表单身份验证票设置自IPrinciple定义HttpContext.Current.User。我的 WCF 服务的构造函数集Thread.CurrentPrinciple = HttpContext.Current.User,因此我的服务可以IPrincipleApplication_AuthenticateRequest.

将安全性更改为 Transport 后,它似乎没有运行,Application_AuthenticateRequest因为 my Thread.CurrentPrincipleis not my custom IPrinciple

有谁知道我如何获得与使用 Transport 之前和使用 Transport 之后相同的行为?

我的网页使用以下内容来引用 WCF 服务:

<asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="
    <Services>
        <asp:ServiceReference Path="~/Services/MyService.svc" />
    </Services>
</asp:ScriptManagerProxy>

我的代码中使用的Application_AuthenticateRequest

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    String cookieName = FormsAuthentication.FormsCookieName;
    HttpCookie authCookie = Context.Request.Cookies[cookieName];

    if (authCookie == null)
    {
        return;
    }

    FormsAuthenticationTicket authTicket = null;
    try
    {
        authTicket = FormsAuthentication.Decrypt(authCookie.Value);
    }
    catch
    {
        return;
    }

    if (authTicket == null)
    {
        return;
    }

    HttpContext.Current.User = new CustomPrinciple(authTicket.Name);
}
4

1 回答 1

1

简单地将安全性从 None 更改为 Transport 不会影响通过 Authenticate_Request 运行的应用程序。您可能应该将调试器附加到服务实现并查看 HttpContext.Current.User 是什么。是您的自定义主体还是未设置?您可能还想在其中添加一些 Debug.Write 消息,看看操作的顺序是什么。Authenticate_Request 事件是否在您预期之后发生?

如果您在配置中使用 ,您可能会在MS Connect 中遇到此问题<serviceAuthorization principalPermissionMode="UseAspNetRoles" />- 此设置似乎与安全传输不兼容,并且 WCF 会覆盖主体。如果您正在设置主体,那么您似乎需要使用principalPermissionMode="None"它。

确保您仍然通过配置 ( ) 或通过服务上的属性( )在服务上启用ASP.NET 兼容模式。否则,该服务将无法访问 HttpContext.Current。<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]

此外,不要在服务实现构造函数中获取 HttpContext.Current.User,而是尝试将其移动到您附加到服务的行为中。实现一个IDispatchMessageInspector处理AfterReceiveRequest事件并将主体从 Web 上下文传输到线程的方法。它看起来像这样:

public class HttpContextPrincipalInspector : IDispatchMessageInspector
{
  public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
  {
    if (HttpContext.Current != null)
    {
      IPrincipal principal = HttpContext.Current.User;
      Thread.CurrentPrincipal = principal;
    }
    return null;
  }

  public void BeforeSendReply(ref Message reply, object correlationState) { }
}

而且,当然,实现一个IEndpointBehavior附加调度程序......

public class HttpContextPrincipalBehavior : IEndpointBehavior
{
  public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
  {
    ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
    foreach (EndpointDispatcher endpointDispatch in channelDispatcher.Endpoints)
    {
      endpointDispatch.DispatchRuntime.MessageInspectors.Add(new HttpContextPrincipalInspector());
    }
  }

  // AddBindingParameters, ApplyClientBehavior, and Validate implementations
  // can be empty - they don't do anything.
}

...和一个自定义BehaviorExtensionElement,以便您可以在 WCF 配置中使用它:

public class HttpContextPrincipalElement : BehaviorExtensionElement
{
  public override Type BehaviorType
  {
    get { return typeof(HttpContextPrincipalBehavior); }
  }

  protected override object CreateBehavior()
  {
    return new HttpContextPrincipalBehavior();
  }
}

然后,您可以将任何服务配置为将其用作端点行为,这样服务实现就不会直接与 Web 上下文对话。使测试更容易一些,并且可以完全解决问题 - 任何时候收到服务的消息,它都会自动转移主体,而不是依赖于服务的构建。

于 2010-02-09T23:56:46.920 回答