I am trying to set wsHttpBinding with username / password.
The problem that I've ran into is how to set username / password?
I've set this:
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
But where is the setting for username / password?
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
Type contractType = typeof(ITest);
Type serviceType = typeof(Test);
Uri httpUri = new Uri("http://localhost:8083/Test2");
ServiceHost myServiceHost = new ServiceHost(serviceType, httpUri);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
myServiceHost.Description.Behaviors.Add(smb);
myServiceHost.Open();
Code:
namespace ConsoleApplication1
{
[ServiceContract]
interface ITest
{
[OperationContract]
string Ping();
}
}
namespace ConsoleApplication1
{
class Test : ITest
{
public string Ping()
{
return "Pong - it works!";
}
}
}
By default WCF uses Windows users to authenticate. But you can plugin your own custom validator.
You need to create your class inherited from UserNamePasswordValidator and then tell WCF to use it:
myServiceHost.Description.Behaviors.Find<ServiceCredentials>().UserNameAuthentication
.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
myServiceHost.Description.Behaviors.Find<ServiceCredentials>().UserNameAuthentication
.CustomUserNamePasswordValidator = new MyCustomValidator();
See also this example and official MSDN documentation.
Related
I need some example of how to implement a RESTfull service with WCF tecnology on a self-host environment and by using a DI container (possibly SimpleInjector).
On https://simpleinjector.readthedocs.io/en/latest/wcfintegration.html i have found how to integrate a custom factory but it's made for ServiceHost but this it's not suitable for a RESTFull service that instead use WebServiceHost?
I tried to configure service host to be compatible with webHttpBinding but nothing happened and i receive this kind of error:
<Fault
xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">
<Code>
<Value>Sender</Value>
<Subcode>
<Value
xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:DestinationUnreachable
</Value>
</Subcode>
</Code>
<Reason>
<Text xml:lang="it-IT">Impossibile elaborare nel destinatario il messaggio con To 'http://localhost:8733/3AdispPushBatchService/pushpost' a causa di una mancata corrispondenza AddressFilter in EndpointDispatcher. Controllare la corrispondenza di EndpointAddresses del mittente e del destinatario.</Text>
</Reason>
</Fault>
There is another integration package to use for WebServiceHost?
This is the example that i have made
AAADispPushBatchService.cs
using System;
using System.Configuration;
using System.IO;
using System.ServiceModel.Activation;
using System.Text;
using Newtonsoft.Json;
namespace AAARestService
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class AAADispPushBatchService : IAaaDispPushBatchService
{
public string GetBatchJson(Stream jsonFileContent)
{
try
{
var sr = new StreamReader(jsonFileContent, Encoding.UTF8);
var str = sr.ReadToEnd();
if (String.IsNullOrWhiteSpace(str))
throw new ArgumentException("No data inside body request");
var definition = new { BatchName = "" };
var json = JsonConvert.DeserializeAnonymousType(str, definition);
if (json == null)
throw new ArgumentException("No valid json inside");
if (String.IsNullOrWhiteSpace(json.BatchName))
throw new ArgumentException("BatchName not present");
var currentDir = ConfigurationManager.AppSettings["BatchPath"];
Directory.CreateDirectory(currentDir);
var filepath = Path.Combine(currentDir, json.BatchName+".json");
File.WriteAllText(filepath, str);
return $"Saved in {filepath}";
}
catch (Exception e)
{
return e.Message;
}
}
}
}
IAaaDispPushBatchService.cs
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace AAARestService
{
[ServiceContract(Name = "AAADispPushBatchService")]
public interface IAaaDispPushBatchService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "pushpost", BodyStyle = WebMessageBodyStyle.Bare,ResponseFormat = WebMessageFormat.Json)]
string GetBatchJson(Stream jsonFileContent);
}
}
EDIT!!!!!
BTW I try to add simpleinjector here are the example based on what i found in the blog
Bootstrapper.cs
using System.Reflection;
using SimpleInjector;
namespace AAARestService
{
public static class BootStrapper
{
public static readonly Container Container;
static BootStrapper()
{
Container container = new Container();
container.Register<IMyDateTimeService,MyDAteTimeService>();
container.RegisterWcfServices(Assembly.GetExecutingAssembly());
Container = container;
}
}
}
MyWebServiceHostFactory.cs
public class MyWebServiceHostFactory : SimpleInjectorServiceHostFactory
{
public ServiceHost GetWebServiceEndpoint(Type serviceType,Uri baseAddress)
{
Uri[] addresses=new Uri[]{baseAddress};
var service = CreateServiceHost(serviceType, addresses);
ServiceEndpoint sep = service.AddServiceEndpoint(typeof(IAaaDispPushBatchService), new WebHttpBinding(), baseAddress);
sep.EndpointBehaviors.Add(new WebHttpBehavior());
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
service.Description.Behaviors.Add(smb);
return service;
}
protected override ServiceHost CreateServiceHost(Type serviceType,
Uri[] baseAddresses)
{
var host = new SimpleInjectorServiceHost(
BootStrapper.Container,
serviceType,
baseAddresses);
return host;
}
}
Main
static void Main(string[] args)
{
try
{
Uri httpUrl = new Uri("http://localhost:8733/3AdispPushBatchService");
Uri httpUrl1 = new Uri("http://localhost:8734/3AdispPushBatchService");
//ServiceHost selfhost = new ServiceHost(typeof(AAADispPushBatchService), httpUrl);
//ServiceEndpoint sep =selfhost.AddServiceEndpoint(typeof(IAaaDispPushBatchService),new WebHttpBinding(), httpUrl);
//sep.EndpointBehaviors.Add(new WebHttpBehavior());
//ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
//smb.HttpGetEnabled = true;
//selfhost.Description.Behaviors.Add(smb);
MyWebServiceHostFactory factory = new MyWebServiceHostFactory();
var selfhost=factory.GetWebServiceEndpoint(typeof(AAADispPushBatchService), httpUrl);
selfhost.Open();
//WebServiceHost webHost = new WebServiceHost(typeof(AAADispPushBatchService),httpUrl1);
//webHost.Open();
foreach (ServiceEndpoint se in selfhost.Description.Endpoints)
Console.WriteLine("Service is host with endpoint " + se.Address);
//foreach (ServiceEndpoint se in webHost.Description.Endpoints)
// Console.WriteLine("Service is host with endpoint " + se.Address);
//Console.WriteLine("ASP.Net : " + ServiceHostingEnvironment.AspNetCompatibilityEnabled);
Console.WriteLine("Host is running... Press <Enter> key to stop");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
I haven't tried integration with WCF using dependency injection technology, maybe the solution in that blog is worth a try. I also don't recommend you use dependency injection in WCF, because it does not support dependency injection natively. In addition, for servicehost hosting WCF REST-style services, we need to add WebHttp behavior on the service endpoint. Please refer to the following code implementation.
Uri uri = new Uri("http://localhost:8004");
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.None;
using (ServiceHost sh = new ServiceHost(typeof(MyService), uri))
{
ServiceEndpoint se=sh.AddServiceEndpoint(typeof(IService), binding,"");
se.EndpointBehaviors.Add(new WebHttpBehavior());
sh.Opened += delegate
{
Console.WriteLine("Service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("Service is clsoed");
};
sh.Open();
Console.ReadLine();
//pause
sh.Close();
Console.ReadLine();
Feel free to let me know if there is anything I can help with.
I am looking for a dotnet core WCF wsHttpBinding workaround.
I am aware that .net core WCF implementation currently does not support wsHttpBinding (see support matrix here https://github.com/dotnet/wcf/blob/master/release-notes/SupportedFeatures-v2.1.0.md)
I'm integrating with a legacy third party service that appears to only support wsHttpBinding. Our tech stack is .net core, so I cannot revert to the full version of .net framework or mono variant.
The question is whether it's possible to use the service via custom bindings? I am hoping that there is a workaround that maybe isn't fully functional, but at least allows me to consume the service.
var cBinding = new CustomBinding();
var textBindingElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.Soap12WSAddressing10
};
cBinding.Elements.Add(textBindingElement);
var httpBindingElement =
new HttpsTransportBindingElement
{
AllowCookies = true, MaxBufferSize = int.MaxValue, MaxReceivedMessageSize = int.MaxValue,
};
cBinding.Elements.Add(httpBindingElement);
var myEndpoint = new EndpointAddress("https://..../Service.svc/wss");
using (var myChannelFactory = new ChannelFactory<ISearch>(cBinding, myEndpoint))
{
myChannelFactory.Credentials.UserName.UserName = "...";
myChannelFactory.Credentials.UserName.Password = "...";
ISearch client = null;
try
{
client = myChannelFactory.CreateChannel();
var result = client.Find(new Search("Criteria")).Result;
((ICommunicationObject)client).Close();
myChannelFactory.Close();
}
catch (Exception ex)
{
(client as ICommunicationObject)?.Abort();
}
}
Client gets created and a call is made to the service, but it fails because:
Message = "The message could not be processed. This is most likely because the action '' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between binding
Forget it, they are not completely compatible with Core. Under some specified case, there may be a basic call to WsHttpBinding. You could refer to the following example.
Server.
Uri uri = new Uri("http://localhost:11011");
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.None;
using (ServiceHost sh=new ServiceHost(typeof(MyService),uri))
{
sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb==null)
{
smb = new ServiceMetadataBehavior()
{
};
sh.Description.Behaviors.Add(smb);
}
Binding mexbinding = MetadataExchangeBindings.CreateMexHttpBinding();
sh.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");
sh.Opened += delegate
{
Console.WriteLine("Service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("Service is clsoed");
};
sh.Open();
Client(auto-generated)
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WSHttpBinding_IService))
{
System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
result.Elements.Add(textBindingElement);
System.ServiceModel.Channels.HttpTransportBindingElement httpBindingElement = new System.ServiceModel.Channels.HttpTransportBindingElement();
httpBindingElement.AllowCookies = true;
httpBindingElement.MaxBufferSize = int.MaxValue;
httpBindingElement.MaxReceivedMessageSize = int.MaxValue;
result.Elements.Add(httpBindingElement);
return result;
}
Invocation.
ServiceReference1.ServiceClient client2 = new ServiceReference1.ServiceClient();
try
{
var res = client2.SayHelloAsync();
Console.WriteLine(res.Result);
}
catch (Exception)
{
throw;
}
While in most cases it is impossible to call WCF service created by WsHttpBinding.
Officials also have no intention of continuing to support plan for wshttpbinding.
Here are related discussions.
https://github.com/dotnet/wcf/issues/31
https://github.com/dotnet/wcf/issues/1370
I have been through WCF concepts, read many and created SELF HOSTED service.
I added library to solution "CommunicationLibrary" which contains:
[ServiceContract]
interface ICommunication
{
[OperationContract]
string message();
}
and
public class Communication: ICommunication
{
public string message()
{
return "WCF Method Accessed";
}
}
then I added console project to host it "WCFCommunicationHosting" which contains
class WcfHost
{
static void Main(string[] args)
{
Uri baseadd = "sasas";
ServiceHost sh = new ServiceHost(typeof(CommunicationLibrary.Communication), baseadd);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
sh.Description.Behaviors.Add(smb);
sh.Open();
Console.WriteLine("Host Running");
}
}
now I don't understand that what to pass in baseAdd? I saw many examples like this CodeProject article but I don't understand that why and how they are passing localhost://.
Here is a working example. Its in vb.net, but you should get the idea of how/what is implemented.
Your service address is the address that you will specify in the connection / binding properties on your client that will consume the service. This is the address where it will find and communicate with your web service.
Dim LocalIpAddress as string = "your pc ip"
Dim tcp_port as string = "the port you want to use"
Dim myServiceAddress As New Uri("http://" & localIpAddress & ":" & tcp_port & "/" & servicename)
myservicehost = New ServiceHost(GetType(yourwcfservicemethodsclass), myServiceAddress)
Dim BasicBinding As New BasicHttpBinding
BasicBinding.MaxReceivedMessageSize = 2147483647
myservicehost.AddServiceEndpoint(GetType(Iyourwcfservicemethodsclass), BasicBinding, myServiceAddress)
' Enable metadata publishing.
Dim smb As New ServiceMetadataBehavior()
smb.HttpGetEnabled = True
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15
myservicehost.Description.Behaviors.Add(smb)
myservicehost.Open()
I have to following code:
BasicHttpBinding binding = new BasicHttpBinding ();
Uri baseAddress = new Uri ("URL.svc");
EndpointAddress endpointAddress = new EndpointAddress (baseAddress);
var myChannelFactory = new ChannelFactory<IMyInterface> (binding, endpointAddress);
IMyInterface client = null;
try
{
client = myChannelFactory.CreateChannel ();
var a = client.WsFunction ("XXXXXX");
((ICommunicationObject)client).Close ();
}
catch
{
if (client != null)
{
((ICommunicationObject)client).Abort ();
}
}
Where "IMyInterface" is the interface that my WS implements.. for example:
[ServiceContract]
public interface IMyInterface
{
[OperationContract]
Result WsFunction1 (string param);
[OperationContract]
Result WsFunction2 (string param);
[OperationContract]
Result WsFunction3 (string param);
}
And it returns something like this:
[DataContract]
public class Result
{
string a = "";
string b = "";
[DataMember]
public string A
{
get { return a; }
set { a = value; }
}
[DataMember]
public string B
{
get { return b; }
set { b = value; }
}
}
When I run this code, I can reach the WS, but I can never get the Result filled out.
What am I doing wrong?
Thanks in advance!
The easiest way to access a service via a BasicHttpBinding is to generate the client code from SlSvcUtil.exe, which is a silverlight utility application.
SLsvcUtil.exe /directory:C:\users\me\Desktop http://URL.svc
That should create a MyInterfaceClient class inside of the file it generates.
Then in your code you can do:
var binding = new BasicHttpBinding() {
Name = "BindingName",
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647
};
var endpoint = new EndpointAddress("URL.svc");
MyInterfaceClient client = new MyInterfaceClient(binding, endpoint);
client.WSFunctionCompleted += (object sender, WSFunctionCompletedEventArgs e) => {
//access e.Result here
};
client.WSFunctionAsync("XXXXXX");
Your mileage may vary. Let me know if this works.
var binding = new BasicHttpBinding();
binding.ProxyAddress = new Uri(string.Format("http://{0}:{1}", proxyAddress, proxyPort));
binding.UseDefaultWebProxy = false;
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
var endpoint = new EndpointAddress("serviceadress");
var authenticationClient = new WOKMWSAuthenticateClient(binding, endpoint);
authenticationClient.ClientCredentials.UserName.UserName = username;
authenticationClient.ClientCredentials.UserName.Password = password;
if you want to run it on your local you should this code.
ServicePointManager.Expect100Continue = false;
Very easy and simple way to call WCF :
BasicHttpBinding myBinding = new BasicHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://localhost:3283/Service1.svc");
myBinding.ReaderQuotas.MaxArrayLength = int.MaxValue;
myBinding.MaxBufferSize = int.MaxValue;
myBinding.MaxReceivedMessageSize = int.MaxValue;
ChannelFactory<ITestAPI> myChannelFactory = new ChannelFactory<ITestAPI>(myBinding, myEndpoint);
ITestAPI instance = myChannelFactory.CreateChannel();
Test data = new Test();
data.helloName = name;
data= instance.GetMyName(data);
myChannelFactory.Close();
As I described in another question I build a web service that will take username/password and based on these credentials authenticate users (mobile apps) in ADFS2. My web service is configured as RP on the ADFS. ADFS issues SAML 2.0 tokens.
Here is a code of the web method:
public class MobileAuthService : IMobileAuthService
{
private const string adfsBaseAddress = #"https://<my_adfs_hostname>/adfs/services/";
private const string endpointSuffix = #"trust/13/issuedtokenmixedsymmetricbasic256";
public string AuthenticateUser(string username, string password)
{
var binding = new WS2007HttpBinding(SecurityMode.Message);
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
var trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress(adfsBaseAddress + endpointSuffix))
{
TrustVersion = TrustVersion.WSTrust13
};
trustChannelFactory.Credentials.UserName.UserName = username;
trustChannelFactory.Credentials.UserName.Password = password;
var tokenClient = (WSTrustChannel)trustChannelFactory.CreateChannel();
var rst = new RequestSecurityToken(RequestTypes.Issue, KeyTypes.Symmetric);
var token = tokenClient.Issue(rst);
// do some token-related stuff
return token.Id;
}
}
When I try to run it (GET call from browser since it's configured with web http binding for this endpoint) I get the following exception:
System.ServiceModel.Security.MessageSecurityException - "An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail."
with inner exception:
System.ServiceModel.FaultException - "An error occurred when verifying security for the message."
I guess it's related with the response signature or certificate but I have no idea how to overcome this since I'm quite new in WIF.
I've managed to (partly) solve this issue. I've changes few things in my code, but the problems seems to be related with:
STS endpoint -should be /trust/13/usernamemixed for this type of authentication
RST key type - when I've set it Bearer it started returning a SAML token
Here is my most recent version:
public class MobileAuthService : IMobileAuthService
{
private const string stsEndpointAddress = #"https://<my_adfs_hostname>/adfs/services/trust/13/usernamemixed";
private const string relyingPartyAddress =
"https://<my_service_addr>/Auth.svc";
public string AuthenticateUser(string username, string password)
{
var binding = new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential)
{
ClientCredentialType = HttpClientCredentialType.None
};
var trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress(stsEndpointAddress))
{
TrustVersion = TrustVersion.WSTrust13
};
var channelCredentials = trustChannelFactory.Credentials;
channelCredentials.UserName.UserName = username;
channelCredentials.UserName.Password = password;
channelCredentials.SupportInteractive = false;
var tokenClient = (WSTrustChannel)trustChannelFactory.CreateChannel();
var rst = new RequestSecurityToken(RequestTypes.Issue, KeyTypes.Bearer)
{
AppliesTo = new EndpointReference(relyingPartyAddress),
ReplyTo = relyingPartyAddress,
TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"
};
// to some token-related stuff (like transformations etc...)
}
}
I hope this will help people who ends up with similar problem.