Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Authentifizierung in Web Services - WS-Security, Benutzername / Passwort

Geschrieben von: Christoph Wille
Kategorie: Web Services

This printed page brought to you by AlphaSierraPapa

Der WS-Security Standard umfasst das Signieren von SOAP Nachrichten, das Verschlüsseln von SOAP Nachrichten, sowie das Anhängen von Security Credentials an SOAP Nachrichten. Diese Security Credentials werden für die Authentifizierung verwendet, und können Benutzername / Passwort, ein X.509 Zertifikat oder Binärtoken sein. Wir beschäftigen uns heute mit ersterem, und werden dies mit Hilfe der Web Services Enhancements (WSE) 1.0 unter .NET implementieren.

Die XML Web Services Downloads Seite enthält die aktuellsten Links zu den Web Service Enhancements. Für diesen Artikel wurden Server und Client-seitig das Web Services Enhancements 1.0 SP1 for Microsoft .NET, und auf der Entwicklermaschine (VS.NET 2002 oder 2003 vorausgesetzt) zusätzlich das Web Services Enhancements Settings Tool for Visual Studio .NET eingesetzt. Bitte installieren Sie diese Tools entsprechend.

Die Benutzername / Passwort Authentifizierung des WS-Security Standards ist im Web Services Enhancements (WSE) über die UsernameToken Klasse implementiert. Der Client generiert diesen Token, schickt ihn im Request an den Server, der Server liest ihn aus dem Request aus - und überprüft, ob der User authentifiziert werden kann. Die Überprüfung des UsernameToken passiert in einem vom Entwickler separat zu erstellenden Password Provider, wo man die Authentifizierung gegen beliebige Datenquellen vornehmen kann.

Der Password Provider für den Web Service

Unser erster Schritt ist also, einen Password Provider (im Download als SamplePasswordProvider enthalten) zu implementieren. Dieser ist eine DLL Assembly, und enthält folgenden Code (SimpleProvider.cs):

using System;
using Microsoft.Web.Services.Security;

namespace SamplePasswordProvider
{
  public class Simple : IPasswordProvider
  {
    public string GetPassword(UsernameToken token)
    {
      // Absicherung, damit wir auf alle Fälle einen Token in der Hand haben
      if (null == token)
        throw new ArgumentNullException();

      string strUsername = token.Username;
      return strUsername;
    }
  }
}

Die Funktionalität für WS-Security in den Web Services Enhancements befindet sich im Namespace Microsoft.Web.Services.Security, der über die Assembly Microsoft.Web.Services.dll referenziert wird.

Die Password Provider Klasse selbst implementiert das Interface IPasswordProvider, das nur eine einzige Methode hat: GetPassword. Damit wird für einen Benutzernamen das Passwort geliefert - den Vergleich des Passwortes nimmt die WSE Infrastruktur selbst vor, da Passwörter ja gehasht übertragen werden können (oder mittels Decryption Key Provider beliebig verschlüsselt).

Kompilieren Sie die Assembly, und merken Sie vor, daß Sie die Assembly in das bin Verzeichnis der Web Service Applikation kopieren müssen!

Der Web Service

Erzeugen Sie einen Web Service in Visual Studio .NET, und wie im Assemblyprojekt zuvor, fügen Sie eine Referenz auf die WSE Assembly ein. Nach diesen Vorarbeiten müssen wir die WSE Infrastruktur über die web.config einbinden. Hier kommt das Web Services Enhancements Settings Tool for Visual Studio .NET ins Spiel - dieses kann über das Kontextmenü des Projekts aufgerufen werden ("WSE Settings"):

Die erste Tabseite legt fest, ob und welche Features von WSE wir verwenden wollen. Diese Einstellungen passen uns gut, und wir gehen zum Security Tab:

Dort geben wir die Assembly sowie den vollen Klassennamen unseres Password Providers an (den Sie in das bin Verzeichnis kopieren). Nach diesem Point-and-Click "Abenteuer" modifiziert uns das Tool automatisch die web.config (Achtung: die Strings type sind nur der Leserlichkeit wegen auf mehrere Zeilen aufgeteilt, in einer lauffähigen web.config müssen diese in einer einzigen Zeile stehen!):

<configuration>
  <configSections>
      <section name="microsoft.web.services"
               type="Microsoft.Web.Services.Configuration.WebServicesConfiguration, 
               Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, 
               PublicKeyToken=31bf3856ad364e35" />
  </configSections>

  <microsoft.web.services>
   <security>
      <!--namespace.classname, assembly name -->
      <passwordProvider type="SamplePasswordProvider.Simple, SamplePasswordProvider" />
   </security>
  </microsoft.web.services> 
  
  <system.web>
   <webServices>
      <soapExtensionTypes>
            <add type="Microsoft.Web.Services.WebServicesExtension,Microsoft.Web.Services,
            Version=1.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" 
            priority="1" group="0" />
      </soapExtensionTypes>
   </webServices>

Nach dieser Vorarbeit kommen wir endlich zum Code des Web Services (Projekt UsernameTokenAuth), beginnen wir mit den zusätzlichen Namespace Imports:

using Microsoft.Web.Services;
using Microsoft.Web.Services.Security;
using System.Web.Services.Protocols;

Die ersten beiden sind für das WSE, der dritte für die SoapException Klasse, mit der wir Fehler an den Client zurückmelden. Sehen wir uns nun unsere Web Service Methode an:

[WebMethod]
public string UsernameTokenAuthSampleMethod()
{
  UsernameToken utCurrent = Authenticate();
  return "Hello World, " + utCurrent.Username;
}

Die ganze Arbeit ist wieder in eine Methode namens Authenticate ausgelagert. Im Vergleich zu früheren Beispielen macht sie aber nur Hilfsarbeiten, da die eigentliche Authentifizierung durch WSE und unseren Password Provider erledigt wird. Trotzdem ist der Sourcecode interessant und vor allem auch notwendig:

private UsernameToken Authenticate()
{
   // Wir müssen 3 Checks durchführen 
   //   * SoapContext muß existieren (SOAP Request & WSE installiert)
   //   * Nur ein UsernameToken darf übergeben worden sein
   //   * Ein Passwort muß übergeben worden sein (Kontrolle auf SendHashed)

   SoapContext sc = HttpSoapContext.RequestContext;
   
   // CHECK 1
   if (null == sc)
   {
      throw new ApplicationException("Nur SOAP Requests erlaubt");
   }

   // CHECK 2
   UsernameToken utCurrent = null;
   foreach (SecurityToken st in sc.Security.Tokens)
   {
      if (st is UsernameToken)
      {
         if (null != utCurrent)
         {
            // ein zweiter Token wurde übergeben - Abbruch
            throw new SoapException("Nur ein UsernameToken pro Request erlaubt!",
                              SoapException.ClientFaultCode);
         }
         else
            utCurrent = (UsernameToken)st;
      }
   }
   if (null == utCurrent)
      throw new SoapException("Kein UsernameToken übergeben", 
                        SoapException.ClientFaultCode);

   // CHECK 3
   if (utCurrent.PasswordOption != PasswordOption.SendHashed)
      throw new SoapException("Falsche Übergabeart für Passwort", 
                        SoapException.ClientFaultCode);

   // alles ok ab hier
   return utCurrent;
}

Die drei Fehlerfälle sind leicht erklärt: der erste ist, daß am Client der Webservice ohne WSE aufgerufen wurde, also ein normaler unauthentifizierter Request gestartet wurde. Der zweite Fall ist, daß mehr als ein UsernameToken übergeben wurde - gegen welchen soll authentifiziert werden? Da wir das nicht wissen, brechen wir sicherheitshalber ab. Der dritte Fall ist, daß auf der Clientseite das Passwort nicht gehashed verschickt wurde, also ein leeres Passwort theoretisch möglich wäre - dies ist aus Sicherheitsgründen auch zu unterbinden.

Nach all diesen zusätzlichen Checks kann man sicher sein, daß sicherheitstechnisch alles wasserdicht ist. Damit haben wir einen abgesicherten Service, und können uns dem Client zuwenden.

Der Client

Das Erstellen der Web Referenz funktioniert automatisch, wenn man das Web Services Enhancements Settings Tool for Visual Studio .NET installiert hat. Wenn nicht, muß man sicherstellen, daß im Web Service Proxy die Klasse so definiert ist:

public class Service1 : Microsoft.Web.Services.WebServicesClientProtocol {

Die Basisklasse ist geändert - das Tool erledigt das selbständig, ohne Tool muß man diese Änderung per Hand machen. Noch das WSE als Referenz eingebunden, und schon kann man den Client coden (Projekt UsernameTokenAuthClient):

using System;

// Notwendig für WSE
using Microsoft.Web.Services.Security;

namespace UsernameTokenAuthClient
{
   class ConsoleAppWsClient
   {
      [STAThread]
      static void Main(string[] args)
      {
         localhost.Service1 svc = new localhost.Service1();
         
         // Neuen UsernameToken anlegen, und der Tokens Collection des Request Contexts anhängen
         UsernameToken ut = new UsernameToken("test", "test", PasswordOption.SendHashed);
         svc.RequestSoapContext.Security.Tokens.Add(ut);

         // Und jetzt den Web Service aufrufen
         string strRetVal = svc.UsernameTokenAuthSampleMethod();
         Console.WriteLine(strRetVal);
      }
   }
}

Hierbei ist wichtig darauf zu achten, den RequestSoapContext zu verwenden, weil es gibt auch den ResponseSoapContext - wenn man bei IntelliSense nicht genau schaut, kann das zu einer langwierigen Fehlersuche führen! Ansonsten ist der Code sehr ähnlich zu dem, den wir für Custom SOAP Headers verwendet haben.

Dank WSE ist die Implementierung einer Benutzername / Passwort Authentifizierung nach dem WS-Security Standard keine große Affäre - im Prinzip ist es ein Kochrezept, das immer angewendet wird. Die aktuellen SOAP Toolkits unterstützen diesen Standard bereits - auf verschiedensten Plattformen und für verschiedenste Programmiersprachen.

Schlußbemerkung

Die Benutzername / Passwort Authentifizierung ist eine gute - vor allem standardisierte - Möglichkeit, Authentifizierung bei Web Services zu implementieren. Doch WS-Security bietet noch mehr Authentifizierungsmethoden, und eine davon werden wir im nächsten Artikel beleuchten: X.509 Zertifikate für die Authentifizierung.

This printed page brought to you by AlphaSierraPapa

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20030502.zip

Verwandte Artikel

Authentifizierung in Web Services - SOAP Header
http:/www.aspheute.com/artikel/20030501.htm
Authentifizierung in Web Services - Windows Integrated
http:/www.aspheute.com/artikel/20030429.htm

Links zu anderen Sites

Web Services Enhancements 1.0 SP1 for Microsoft .NET
http://www.microsoft.com/downloads/details.aspx?FamilyId=06255A94-2635-4D29-A90C-28B282993A41&displaylang=en
Web Services Enhancements Settings Tool for Visual Studio .NET
http://www.microsoft.com/downloads/details.aspx?FamilyId=E1924D29-E82D-4D9A-A945-3F074CE63C8B&displaylang=en
XML Web Services Downloads
http://msdn.microsoft.com/library/default.asp?url=/downloads/list/websrv.asp

 

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.