Exchanging Data using SOAP Headers

 
Chapter 15 - Web Services
bySimon Robinsonet al.
Wrox Press 2002
  

One final topic to look at in this chapter is using SOAP headers to exchange information, rather than including information in method parameters. The reason for covering it is that it is a very nice system to use for maintaining a user login. We won't go into detail about setting up your server for SSL connections, or the various methods of authentication that can be configured using IIS, as these do not affect the Web Service code we need to get this behavior.

The situation is as follows. Let's say we have a service that contains a simple authentication method with a signature as follows :

   AuthenticationToken AuthenticateUser(string userName, string password);   

Where AuthenticationToken is a type we define that can be used by the user in later method calls, for example:

   void DoSomething(AuthenticationToken token, OtherParamType param);   

Once a user has 'logged on' they then have access to other methods using the token they receive from AuthenticateUser() . This technique is typical of secure web systems, although it may be implemented in a far more complex way.

We can simplify this further by using a SOAP header to exchange tokens (or any other data). We can restrict methods such that they may only be called if a specified SOAP header is included in the method call, thus simplifying their structure to something along the lines of:

   void DoSomething(OtherParamType param);   

The advantage here is that, once we have set the header on the client, it persists, so after an initial bit of setting up we can ignore authentication tokens in all further web method calls.

To see this in action create a new Web Service project called PCSWebSrv3 , and add a new class called AuthenticationToken.cs as follows:

   using System;     using System.Web.Services.Protocols;     namespace PCSWebSrv3     {     public class AuthenticationToken : SoapHeader     {     public Guid InnerToken;     }     }   

We'll use a GUID to identify the token, a common procedure, as we can be sure that it is unique.

To declare that the Web Service can have a custom SOAP header we simply add a public member to the service class, of our new type:

 public class Service1 : System.Web.Services.WebService    {   public AuthenticationToken AuthenticationTokenHeader;   

We also need a using statement for System.Web.Services.Protocols in the Service1.asmx.cs file. This namespace contains an attribute called SoapHeaderAttribute which we can use to mark those web methods that require the extra SOAP header in order to work.

Before we add such a method though, let's add a very simple Login() method that clients can use to obtain an authentication token:

   [WebMethod]     public Guid Login(string userName, string password)     {     if ((userName == "Karli") && (password == "Cheese"))     {     Guid currentUser = Guid.NewGuid();     Application["currentUser"] = currentUser;     return currentUser;     }     else     {     Application["currentUser"] = Guid.Empty;     return Guid.Empty;     }     }   

If the correct username and password are used then a new Guid is generated, stored in an Application-level variable, and returned to the user. If authentication fails then an empty Guid is returned and stored at the Application level.

Next we have a method that requires the header, as specified by the SoapHeaderAttribute attribute:

   [WebMethod]     [SoapHeaderAttribute("AuthenticationTokenHeader",     Direction = SoapHeaderDirection.In,     Required = true)]     public string DoSomething()     {     if ((AuthenticationTokenHeader.InnerToken     == (Guid)Application["currentUser"]) &&     (AuthenticationTokenHeader.InnerToken != Guid.Empty))     {     return "Authentication OK.";     }     else     {     return "Authentication failed.";     }     }   

This returns one of two strings, depending on whether the required header isn't an empty Guid and matches the one stored in Application["currentUser"] .

Next we need to create a quick client to test this service. Create a new web application called PCSWebClient3 , with the following simple code for the user interface:

   <form id="Form1" method="post" runat="server">     User Name:     <asp:TextBox Runat="server" ID="userNameBox" /><br>     Password:     <asp:TextBox Runat="server" ID="passwordBox" /><br>     <asp:Button Runat="server" ID="loginButton" Text="Log in" /><br>     <asp:Label Runat="server" ID="tokenLabel" /><br>     <asp:Button Runat="server" ID="invokeButton" Text="Invoke DoSomething()" /><br>     <asp:Label Runat="server" ID="resultLabel" /><br>     </form>   

Add the PCSWebSrv3 service as a web reference with the folder name authenticateService , and add the following using statement to WebForm1.aspx.cs :

   using PCSWebClient3.authenticateService;   

We will use a protected member to store the web reference proxy, and another to store a Boolean value indicating whether the user is authenticated or not:

 public class WebForm1 : System.Web.UI.Page    {       protected System.Web.UI.WebControls.TextBox userNameBox;       protected System.Web.UI.WebControls.TextBox passwordBox;       protected System.Web.UI.WebControls.Button loginButton;       protected System.Web.UI.WebControls.Label tokenLabel;       protected System.Web.UI.WebControls.Button invokeButton;       protected System.Web.UI.WebControls.Label resultLabel;   protected Service1 myService;     protected bool authenticated;   

We will initialize this member in Page_Load() . Once we have a header to use with this Web Service we'll store it in the ViewState collection of the form (a useful way to persist information between postbacks, which works in a similar way to storing information at the Application or Session level). Page_Load() looks to see if there is a stored header and assigns the header to the proxy accordingly (assigning the header in this way is the only step we need to take for the data to be sent as a SOAP header). This way any event handlers that are being called (such as the one for the web method invoking button) won't need to worry about assigning a header  that step has already been taken:

 private void Page_Load(object sender, System.EventArgs e)       {   myService = new Service1();     AuthenticationToken header = new AuthenticationToken();     if (ViewState["AuthenticationTokenHeader"] != null)     {     header.InnerToken = (Guid)ViewState["AuthenticationTokenHeader"];     }     else     {     header.InnerToken = Guid.Empty;     }     myService.AuthenticationTokenValue = header;   } 

Next we add an event handler for the Log in button by double-clicking on it in the Designer:

 private void loginButton_Click(object sender, System.EventArgs e)       {   Guid authenticationTokenHeader = myService.Login(userNameBox.Text,     passwordBox.Text);     tokenLabel.Text = authenticationTokenHeader.ToString();     ViewState.Add("AuthenticationTokenHeader", authenticationTokenHeader);   } 

This handler uses any data entered in the two text boxes to call Login() , displays the Guid returned, and stores the Guid in the ViewState collection.

Finally, we have to add a handler in the same way for the Invoke DoSomething() button:

 private void invokeButton_Click(object sender, System.EventArgs e)       {   resultLabel.Text = myService.DoSomething();   } 

This handler simply outputs the text returned by DoSomething() .

When we run this application we can press the Invoke DoSomething() button straight away, as Page_Load() has assigned the correct header (if we haven't assigned a header then an exception will be thrown, as we have specified that the header is required for this method). This will result in a failure message, returned from DoSomething() :

click to expand

If we try to log in with any user name and password except " Karli " and " Cheese " we will get the same result. If, on the other hand, we log in using these credentials and then call DoSomething() we get the success message:

click to expand

We can also see the Guid used for validation.

Of course, applications that use this technique of exchanging data via SOAP headers are likely to be far more complicated. They will need to store login tokens in a more sensible way than just one Application-level variable, perhaps in a database. For completeness we can also expire these tokens when a certain amount of time has passed, and provide the facility for users to log out if they wish, which would simply mean removing the token. We could even validate the token against the IP address used by the user for further security. The key points here though are that the username and password of the user are only sent once, and that using a SOAP header simplifies later method calls.

  


Professional C#. 2nd Edition
Performance Consulting: A Practical Guide for HR and Learning Professionals
ISBN: 1576754359
EAN: 2147483647
Year: 2002
Pages: 244

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net