Here's the situation. I have a webservice (C# 2.0), which consists of (mainly) a class inheriting from System.Web.Services.WebService. It contains a few methods, which all need to call a method that checks if they're authorized or not.
Basically something like this (pardon the architecture, this is purely as an example):
public class ProductService : WebService
{
public AuthHeader AuthenticationHeader;
[WebMethod(Description="Returns true")]
[SoapHeader("AuthenticationHeader")]
public bool MethodWhichReturnsTrue()
{
if(Validate(AuthenticationHeader))
{
throw new SecurityException("Access Denied");
}
return true;
}
[WebMethod(Description="Returns false")]
[SoapHeader("AuthenticationHeader")]
public bool MethodWhichReturnsFalse()
{
if(Validate(AuthenticationHeader))
{
throw new SecurityException("Access Denied");
}
return false;
}
private bool Validate(AuthHeader authHeader)
{
return authHeader.Username == "gooduser" && authHeader.Password == "goodpassword";
}
}
As you can see, the method Validate
has to be called in each method. I'm looking for a way to be able to call that method, while still being able to access the soap headers in a sane way. I've looked at the events in the global.asax
, but I don't think I can access the headers in that class... Can I?
Here is what you need to do to get this to work correctly.
It is possible to create your own custom SoapHeader:
public class ServiceAuthHeader : SoapHeader
{
public string SiteKey;
public string Password;
public ServiceAuthHeader() {}
}
Then you need a SoapExtensionAttribute:
public class AuthenticationSoapExtensionAttribute : SoapExtensionAttribute
{
private int priority;
public AuthenticationSoapExtensionAttribute()
{
}
public override Type ExtensionType
{
get
{
return typeof(AuthenticationSoapExtension);
}
}
public override int Priority
{
get
{
return priority;
}
set
{
priority = value;
}
}
}
And a custom SoapExtension:
public class AuthenticationSoapExtension : SoapExtension
{
private ServiceAuthHeader authHeader;
public AuthenticationSoapExtension()
{
}
public override object GetInitializer(Type serviceType)
{
return null;
}
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(SoapMessage message)
{
if (message.Stage == SoapMessageStage.AfterDeserialize)
{
foreach (SoapHeader header in message.Headers)
{
if (header is ServiceAuthHeader)
{
authHeader = (ServiceAuthHeader)header;
if(authHeader.Password == TheCorrectUserPassword)
{
return; //confirmed
}
}
}
throw new SoapException("Unauthorized", SoapException.ClientFaultCode);
}
}
}
Then, in your web service add the following header to your method:
public ServiceAuthHeader AuthenticationSoapHeader;
[WebMethod]
[SoapHeader("AuthenticationSoapHeader")]
[AuthenticationSoapExtension]
public string GetSomeStuffFromTheCloud(string IdOfWhatYouWant)
{
return WhatYouWant;
}
When you consume this service, you must instantiate the custom header with the correct values and attach it to the request:
private ServiceAuthHeader header;
private PublicService ps;
header = new ServiceAuthHeader();
header.SiteKey = "Thekey";
header.Password = "Thepassword";
ps.ServiceAuthHeaderValue = header;
string WhatYouWant = ps.GetSomeStuffFromTheCloud(SomeId);
You can implement the so-called SOAP extension by deriving from SoapExtension base class. That way you will be able to inspect an incoming SOAP message and perform validate logic before a particular web method is called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With