How to pass WCF UserName clientCredentialType to o

2019-07-29 08:10发布

问题:

I have several WCF services hosted in IIS6 (should not affect this issue) on the same host, and I want, for Performance/ Maintanance and other reasons to combine several requests into 1 request using a Facade Service, All done with special Service Contract / Service that has an operation that calls other services for several operations.

I'm using WSHTTP (probably BasicHttp in the near future) with Message security and UserName client credential type.

I want the Facade Service to use the credentials from the client. Meaning the call to the back-end service will get the credentials as if the client would call it directly.

For example: Client calls FacadeService.CompositeOperation with UserName "A" and password "B". Now the FacadeService.CompositeOperation needs to call BackEndService.BackendOperation setting the Credentials.UserName.UserName to "A" and Credentials.UserName.Password to "B" just like what the client done when calling to this operation. I have no way to extract this information in WCF (and it should be, because it is sensitive information) but i neither found a way to take "a token" of these and pass it forward to the backend service (I have no need to know this information in the FacadeService, only to pass them over).

In FacadeService, as in BackEndService, the authentication is made through ASP.NET provider, the authorization is a custom Role-based authorization taking the UserName from the PrimaryIdentity, so the PrimaryIdentity on the BackEndService should be set to what the client send.

How should i do it?

回答1:

I read your post yesterday but wasn't sure of an answer, but seeing as you've had no replies i thought i'd add something and maybe provide some food for thought.

  • Firstly, would making the additonal service calls be overly intensive on resources? If not, there is an argument for code clarity, to seperate them out so in the future developers will know exactly what's happening rather than 1 service call performing multiple operations.

  • Are you not able to make calls to other services from your server side code from within the method you're hitting? As once, you're server side, the security context should hold the identity of the user that you're after so calls to other services would use the same identity.

  • Finally, I was wondering whether WCF Impersonation (MSDN LINK) might be something you can use on the server to achieve what you're after. I've not used it myself so can't advise as much as i'd like.

Hope that's of some help - good luck!



回答2:

Once i tried to Store Password along with UserName in PrimaryIdentity. To achieve this What we need to do is to provide a New UserNameSecurityTokenAuthenticator Which will authenticate UserName and Password and then can store in the Identity and then it will Store the Identity in SecurityContext of WCF.

Steps to Do

Classes

1.) TestServiceHost : ServiceHost

2.) UserNamePasswordSecurityTokenManager : ServiceCredentialsSecurityTokenManager

3.) TestUserNameSecurityTokenAuthenticator : UserNameSecurityTokenAuthenticator

4.) MyIdentity : IIdentity

5.) MyAuthorizatoinPolicy : IAuthorizationPolicy

1.) Create New ServiceHost class TestServiceHost

2.) In TestServiceHost Override OnOpening and provide a new Class UserNamePasswordServiceCredentials

protected override void OnOpening()
{
    base.OnOpening();
    this.Description.Behaviors.Add(new UserNamePasswordServiceCredentials());
}

3.) Then in UserNamePasswordServiceCredentials, provide new UserNamePasswordSecurityTokenManager

public override SecurityTokenManager CreateSecurityTokenManager()
{
    return new UserNamePasswordSecurityTokenManager(this);
}

4.) Then in UserNamePasswordSecurityTokenManager provide new TestUserNameSecurityTokenAuthenticator

public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            if (tokenRequirement.TokenType == SecurityTokenTypes.UserName)
            {
                outOfBandTokenResolver = null;
                return new TestUserNameSecurityTokenAuthenticator();
            }
            return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
        }

5.) Then Inside TestUserNameSecurityTokenAuthenticator you can Authenticate UseraName and Password and can create your own Identity. In this function you will return a list of IAuthorization policies to be evaluated. I created my own authorization Policy and passed my new identity to it, so as to set the Identity in context.

protected override System.Collections.ObjectModel.ReadOnlyCollection<System.IdentityModel.Policy.IAuthorizationPolicy> ValidateUserNamePasswordCore(string userName, string password)
        {           
            ClaimSet claimSet = new DefaultClaimSet(ClaimSet.System, new Claim(ClaimTypes.Name, userName, Rights.PossessProperty));
            List<IIdentity> identities = new List<IIdentity>(1);
            identities.Add(new MyIdentity(userName,password));
            List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
            policies.Add(new MyAuthorizationPolicy(ClaimSet.System, identities));
            return policies.AsReadOnly();
        }


public class MyAuthorizationPolicy : IAuthorizationPolicy
    {
        String id = Guid.NewGuid().ToString();
        ClaimSet issuer;
        private IList<IIdentity> identities;
        #region IAuthorizationPolicy Members


        public MyAuthorizationPolicy(ClaimSet issuer, IList<IIdentity> identities)
        {
            if (issuer == null)
                throw new ArgumentNullException("issuer");
            this.issuer = issuer;
            this.identities = identities;

        }

        public bool Evaluate(EvaluationContext evaluationContext, ref object state)
        {
            if (this.identities != null)
            {
                object value;
                IList<IIdentity> contextIdentities;
                if (!evaluationContext.Properties.TryGetValue("Identities", out value))
                {
                    contextIdentities = new List<IIdentity>(this.identities.Count);
                    evaluationContext.Properties.Add("Identities", contextIdentities);
                }
                else
                {
                    contextIdentities = value as IList<IIdentity>;
                }
                foreach (IIdentity identity in this.identities)
                {
                    contextIdentities.Add(identity);
                }
            }
            return true;
        }

        public ClaimSet Issuer
        {
            get { return this.issuer; }
        }

        #endregion

        #region IAuthorizationComponent Members

        public string Id
        {
            get { return this.id; }
        }

        #endregion
    }

So this example shows how you can override Security in WCF:

Now in your problem:

1.) Implement this Technique and Set UserName and Password in your identity. Now when ever you have call child service, get Identity extract Username and password from it and pass on to child service.

2.) Authenticate UserName and Password and generate a token for that (should create a new token service for that). Save this Token in your Identity along with Username and pass these two to your child services. Now for this approach to work, child service has to validate your new generated token, for which you should have a token Service which can create token by validating username and password and also which can validate token along with username.

Personally I would go for approach 2, but it will introduce new overheads.