WCF Caching ChannelFactories for Multiple ASP.NET Users

I have an enterprise system that is used by several WinForms clients and an open source ASP.NET site. The internal WCF service provides several services that will be consumed by each of these clients. Services require credentials, which in the case of the WinForms application are provided by the user the first time the program starts.

I cache ChannelFactories in WinForm applications to improve performance. I would like to do the same on an ASP.NET site. However, since ClientCredentials are stored as part of the factory ( ChannelFactory<T>.Credentials), does one ChannelFactory need to be cached for each user? It seems that even with moderate use it will add up quickly. In addition, I believe that I will need to store them at the application level, and not at the session level, because for future scalability I can not guarantee that I will always use the InProc session state.

I see no way to create one ChannelFactory for each service, and then specify credentials when creating the channel. Did I miss something?

+3
source share
2 answers

I just stumbled upon this question unanswered many months later, and finally I can answer it. I went with a solution very similar to the ASP.NET website + Windows Forms application + WCF service: client credentials . Nevertheless, I decided that I would write my approach here.

( ) WCF /, - , . , - X509, WCF . , , ChannelFactory ASP.NET, , WCF, .

ServiceHost, URL- . - TransportWithMessageCredential, - , - .

var usernameBinding = new BasicHttpBinding( BasicHttpSecurityMode.TransportWithMessageCredential )
usernameBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

var certificateBinding = new BasicHttpBinding( BasicHttpSecurityMode.TransportWithMessageCredential )
certificateBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;

var serviceHost = new ServiceHost( new MyService() );
serviceHost.Description.Namespace = "http://schemas.mycompany.com/MyProject";
serviceHost.AddServiceEndpoint( typeof( T ), usernameBinding, "https://myserver/MyProject/MyService" );
serviceHost.AddServiceEndpoint( typeof( T ), certificateBinding, "https://myserver/MyProject/Web/MyService" );

ServiceCredentials : ) , ) ) . , WCF (A + B + C), A + B, A + C.

var serviceCredentials = new ServiceCredentials();
serviceCredentials.ServiceCertificate.SetCertificate( StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "myserver" );
serviceCredentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
serviceCredentials.UserNameAuthentication.CustomUserNamePasswordValidator = this.UserNamePasswordValidator;
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
serviceCredentials.ClientCertificate.SetCertificate( StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "SelfSignedWebsiteCertificate" );
serviceHost.Description.Behaviors.Add( serviceCredentials );

, IAuthorizationPolicy ServiceHost ServiceAuthorizationBehavior. , UserNamePasswordValidator, , IPrincipal , . X509, , , , , . IAuthorzationPolicy.Evaluate:

public bool Evaluate( EvaluationContext evaluationContext, ref object state )
{
    var identity = ((List<IIdentity>) evaluationContext.Properties[ "Identities" ] ).First();

    if ( identity.AuthenticationType == "MyCustomUserNamePasswordValidator" )
    {
        evaluationContext.Properties[ "Principal" ] = new GenericPrincipal( identity, null );
    }
    else if ( identity.AuthenticationType == "X509" )
    {
        var impersonatedUsername = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>( "ImpersonatedUsername", "http://schemas.mycompany.com/MyProject" );

        evaluationContext.AddClaimSet( this, new DefaultClaimSet( Claim.CreateNameClaim( impersonatedUsername ) ) );

        var impersonatedIdentity = new GenericIdentity( impersonatedUsername, "ImpersonatedUsername" );
        evaluationContext.Properties[ "Identities" ] = new List<IIdentity>() { impersonatedIdentity };
        evaluationContext.Properties[ "Principal" ] = new GenericPrincipal( identity, null );
    }
    else
        throw new Exception( "Bad identity" );

    return true;
}

ServiceHost :

serviceHost.Authorization.ExternalAuthorizationPolicies = new List<IAuthorizationPolicy>() { new CustomAuthorizationPolicy() }.AsReadOnly();
serviceHost.Authorization.PrincipalPermissionMode = PrincipalPermissionMode.Custom;

ServiceSecurityContext.Current.PrimaryIdentity , , . WCF. ASP.NET (certificateBinding, ), ChannelFactory. factory.Endpoint.Behaviors, HttpContext.Current.User WCF, . , IClientMessageInspector BeforeSendRequest, ( ):

public object BeforeSendRequest( ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel )
{
    request.Headers.Add( MessageHeader.CreateHeader( "ImpersonatedUsername", "http://schemas.mycompany.com/MyProject", HttpContext.Current.User.Identity.Name ) );

    return null;
}

, IEndpointBehavior, . , ; :

public class GenericClientInspectorBehavior : IEndpointBehavior
{
    public IClientMessageInspector Inspector { get; private set; }

    public GenericClientInspectorBehavior( IClientMessageInspector inspector )
    { Inspector = inspector; }

    // Empty methods excluded for brevity

    public void ApplyClientBehavior( ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime )
    { clientRuntime.MessageInspectors.Add( Inspector ); }
}

, , , ChannelFactory :

factory.Credentials.ClientCertificate.SetCertificate( StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "SelfSignedWebsiteCertificate" );
factory.Endpoint.Behaviors.Add( new GenericClientInspectorBehavior( new HttpContextAuthenticationInspector() ) );

- , HttpContext.Current.User, . -, ChannelFactory, , / ClientCredentials WCF. , , .

FormsAuthentication IPrincipal HttpContext.Current.User. ChannelFactory, _, ChannelFactory, certificateBinding, ASP.NET. ChannelFactory HttpContext.Current.User WCF.

, ChannelFactory WCF ASP.NET, ChannelFactory , . , , .

+4

, WCF , ?

WCF . factory - , WCF , .

factory , WCF ( , .)

, : MSDN

:

public class HelloService : IHelloService
{
    [OperationBehavior]
    public string Hello(string message)
    {
        WindowsIdentity callerWindowsIdentity =
        ServiceSecurityContext.Current.WindowsIdentity;
        if (callerWindowsIdentity == null)
        {
            throw new InvalidOperationException
            ("The caller cannot be mapped to a WindowsIdentity");
        }
        using (callerWindowsIdentity.Impersonate())
        {
           // Access a file as the caller.
        }
        return "Hello";
    }
}
0

Source: https://habr.com/ru/post/1728654/


All Articles