2009-01-23 6 views
0

J'appelle WCF à partir d'une page ASP.NET à l'aide d'ASP.NET Ajax. J'utilise l'authentification par formulaire pour sécuriser le site Web.Utilisation d'ASP.NET Ajax pour appeler WCF sur SSL

Tout fonctionnait comme prévu grâce au développement jusqu'à son déploiement sur le serveur de production, puis j'ai commencé à recevoir des erreurs JavaScript car le service était introuvable. Le serveur de production utilise SSL donc j'ajouté ce qui suit à mon web.config:

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

Cela arrêté les erreurs JavaScript de se produire, mais maintenant le service WCF ne se comporte pas comme il utiliser pour. Avant de définir la sécurité sur Transférer un appel au service WCF à partir d'ASP.NET Ajax exécutera le Application_AuthenticateRequest dans mon Global.asax. Cela permet de configurer un IPrinciple personnalisé sur HttpContext.Current.User en fonction du ticket d'authentification par formulaire. Le constructeur de mon service WCF définit Thread.CurrentPrinciple = HttpContext.Current.User afin que mon service ait accès à l'ensemble IPrinciple pendant Application_AuthenticateRequest.

Après avoir changé la sécurité à Transport il ne semble pas courir Application_AuthenticateRequest car mon Thread.CurrentPrinciple n'est pas mon IPrinciple personnalisé.

Est-ce que quelqu'un sait comment je peux obtenir le même comportement qu'avant en utilisant le transport comme après avoir utilisé le transport?

Ma page Web utilise ce qui suit pour référencer le service WCF:

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

Code utilisé dans mon 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); 
} 

Répondre

1

changer simplement la sécurité de Aucun à Transports ne devrait pas affecter l'application en cours d'exécution via Authenticate_Request. Vous devez probablement attacher le débogueur à l'implémentation de service et voir ce que HttpContext.Current.User est. Est-ce votre mandant personnalisé ou n'est-il pas également défini? Vous pouvez également ajouter deux messages Debug.Write et voir l'ordre des opérations. L'événement Authenticate_Request se produit-il après que vous l'ayez attendu?

Vous pouvez être en cours d'exécution dans this issue from MS Connect si vous utilisez le <serviceAuthorization principalPermissionMode="UseAspNetRoles" /> dans votre configuration - il semble que ce paramètre n'est pas compatible avec un transport sécurisé et WCF remplace le principal. Si vous définissez le principal, il semble que vous deviez utiliser principalPermissionMode="None" à ce sujet.

Assurez-vous que le ASP.NET Compatibility Mode est toujours activé sur le service via config (<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />) ou via les attributs du service ([AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]). Sans cela, le service n'aura pas accès à HttpContext.Current.

Aussi, plutôt que d'obtenir le HttpContext.Current.User dans le constructeur de la mise en œuvre de service, essayez de le déplacer à un comportement que vous attachez au service. Implémentez un IDispatchMessageInspector qui gère l'événement AfterReceiveRequest et transfère le principal du contexte Web au thread. Ça va ressembler à quelque chose comme ceci:

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) { } 
} 

Et, bien sûr, mettre en œuvre un IEndpointBehavior pour fixer le répartiteur ...

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

... et une coutume BehaviorExtensionElement afin que vous puissiez l'utiliser dans la configuration WCF:

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

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

Vous pouvez configurer l'un de vos services pour l'utiliser comme un comportement de point de terminaison de sorte que la mise en œuvre de service ISN Ne parlez pas directement au contexte Web. Cela rend un peu plus facile à tester et peut résoudre le problème entièrement - chaque fois qu'un message arrive pour le service, il transférera automatiquement le principal plutôt que de compter sur la construction du service.