WCF 4.0 – Windows Authentication

I use the Windows Authentication very often in my everyday projects, so decided to write this article as it might be useful for you as well…

My aim was to create one web service with 2 endpoints having different HTTP methods available:

To achieve that you can use the WCF template available in VS 2010 as a start point:

1. Create new VS 2010 project using the Online Template (make sure .Net 4 is selected)

2. Modify the Service1.cs file replacing the original content with the code below:

Make sure that you have replace the WCF_Service_40 namespace with your original namespace.
As you can see we have added another contract class with methods available when user is authenticated.


using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;

namespace WCF_Service_40
{
// Start the service and browse to http://:/Service1/help to view the service's generated help page
// NOTE: By default, a new instance of the service is created for each call; change the InstanceContextMode to Single if you want
// a single instance of the service to process all calls.
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
// NOTE: If the service is renamed, remember to update the global.asax.cs file
public class Public_Service
{
// TODO: Implement the collection resource that will contain the SampleItem instances

[WebGet(UriTemplate = "")]
public List GetCollection()
{
// TODO: Replace the current implementation to return a collection of SampleItem instances
return new List() { new SampleItem() { Id = 1, StringValue = "Hello" } };
}

}

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Admin_Service
{
// TODO: Implement the collection resource that will contain the SampleItem instances

[WebGet(UriTemplate = "")]
public List GetCollection()
{
// TODO: Replace the current implementation to return a collection of SampleItem instances
return new List() { new SampleItem() { Id = 1, StringValue = "THis message can be seen only by authentificated users." } };
}

[WebInvoke(UriTemplate = "", Method = "POST")]
public SampleItem Create(SampleItem instance)
{
// TODO: Add the new instance of SampleItem to the collection
throw new NotImplementedException();
}

[WebGet(UriTemplate = "{id}")]
public SampleItem Get(string id)
{
// TODO: Return the instance of SampleItem with the given id
throw new NotImplementedException();
}

[WebInvoke(UriTemplate = "{id}", Method = "PUT")]
public SampleItem Update(string id, SampleItem instance)
{
// TODO: Update the given instance of SampleItem in the collection
throw new NotImplementedException();
}

[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
public void Delete(string id)
{
// TODO: Remove the instance of SampleItem with the given id from the collection
throw new NotImplementedException();
}

}
}

3. Open Global.asax.cs file and replace it content to:

In the code below we are creating custom WebServiceHostFactoryclass. When registering route we can now add parameter ‘true’ or ‘false’ depending if we want the security to be turned on or off.


private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("soapService", new SoapServiceHostFactory(), typeof(Public_Service)));
RouteTable.Routes.Add(new ServiceRoute("restService", new CustomWebServiceHostFactory(false), typeof(Public_Service)));
RouteTable.Routes.Add(new ServiceRoute("adminService", new CustomWebServiceHostFactory(true), typeof(Admin_Service)));
}

Full code:


using System;
using System.ServiceModel.Activation;
using System.Web;
using System.Web.Routing;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.Web.Configuration;
using System.Configuration;

namespace WCF_Service_40
{
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}

private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("soapService", new SoapServiceHostFactory(), typeof(Public_Service)));
RouteTable.Routes.Add(new ServiceRoute("restService", new CustomWebServiceHostFactory(false), typeof(Public_Service)));
RouteTable.Routes.Add(new ServiceRoute("adminService", new CustomWebServiceHostFactory(true), typeof(Admin_Service)));

}
}

public class SoapServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
host.AddServiceEndpoint(serviceType, new BasicHttpBinding(), "soap");
return host;

}

}

public class CustomWebServiceHostFactory : WebServiceHostFactory
{

private bool security_enabled;
public CustomWebServiceHostFactory(bool security_enabled)
{
this.security_enabled = security_enabled;
}

protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
WebServiceHost host = (WebServiceHost)base.CreateServiceHost(serviceType, baseAddresses);

if (security_enabled == false)
{
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });

ServiceEndpoint xml_endpoint = host.AddServiceEndpoint(serviceType, new WebHttpBinding { }, "XML");
xml_endpoint.Behaviors.Add(new WebHttpBehavior { DefaultBodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Bare, HelpEnabled = true, DefaultOutgoingResponseFormat = WebMessageFormat.Xml });

ServiceEndpoint json_endpoint = host.AddServiceEndpoint(serviceType, new WebHttpBinding { }, "JSON");
json_endpoint.Behaviors.Add(new WebHttpBehavior { DefaultBodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Bare, HelpEnabled = true, DefaultOutgoingResponseFormat = WebMessageFormat.Json });

}
else
{
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });

ServiceCredentials serviceCredential = host.Description.Behaviors.Find();
if (serviceCredential == null)
{
serviceCredential = new ServiceCredentials();
serviceCredential.WindowsAuthentication.AllowAnonymousLogons = false;
serviceCredential.WindowsAuthentication.IncludeWindowsGroups = true;
host.Description.Behaviors.Add(serviceCredential);
}

ServiceEndpoint xml_endpoint = host.AddServiceEndpoint(serviceType, new WebHttpBinding { Security = new WebHttpSecurity { Mode = WebHttpSecurityMode.TransportCredentialOnly, Transport = new HttpTransportSecurity { ClientCredentialType = HttpClientCredentialType.Windows, ProxyCredentialType = HttpProxyCredentialType.Windows } } }, "XML");
xml_endpoint.Behaviors.Add(new WebHttpBehavior { DefaultBodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Bare, HelpEnabled = true, DefaultOutgoingResponseFormat = WebMessageFormat.Xml });

ServiceEndpoint json_endpoint = host.AddServiceEndpoint(serviceType, new WebHttpBinding { Security = new WebHttpSecurity { Mode = WebHttpSecurityMode.TransportCredentialOnly, Transport = new HttpTransportSecurity { ClientCredentialType = HttpClientCredentialType.Windows, ProxyCredentialType = HttpProxyCredentialType.Windows } } }, "JSON");
json_endpoint.Behaviors.Add(new WebHttpBehavior { DefaultBodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Bare, HelpEnabled = true, DefaultOutgoingResponseFormat = WebMessageFormat.Json });

}
return host;

}

}

}

4. Try to access the adminService using debugging:

You should be getting the error from the picture below:

4. To add the windows authentication publish your WCF Service on IIS 7 (Windows Vista, 7 Home edition do not support the Windows authentification – you have to have the proffesional version of the operating system to enable it).

5. Add Blank ‘AdminService’ folder to your deployment folder and enable the windows authentication on it as shown on the picture below:

6. If you still cannot get the AdminServis URI working try to edit the C:\Windows\System32\inetsrv\config\applicationHost.config file changing overrideModeDefault value for Anonymus and Windows authentication type as shown below.

 

7. When it’s configured properly you should be able to access both endpoint using different authentification method.

 

Advertisements

Published by

osedok

I am interested in Android and Web development, Open Street Map editing and looking for new things connected to the maps and spatial data. I enjoy making stuff and understanding how the stuff works. I can code in many programming languages and I am really having fun of it. When having free time I am taking care of my apps and write some content on my blog. I enjoy the countryside, which is giving me opportunities for photography. My favourite motto from Albert Einstein is: "If you can't explain it simply, you don't understand it well enough."

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s