Impersonation in ASP.NET
Wo ist der Unterschied zwischen der Einstellung des für den ASP.NET Prozess verwendeten Kontos durch den <identity.../> Tag und der Identity in der IIS App Domain? - Diese und andere interessante Fragen stellt man sich häufig im Laufe eines Entwicklungsprozesses, meist immer dann, wenn eine Sache auf einmal nicht so läuft, wie man es erwartet hätte. Und wenngleich man vielleicht alle Teilbereiche zu verstehen glaubt, so ist die Gesamtheit oftmals überraschend. So auch in diesem Fall, wo beim Deployment Prozess auf einmal diverse Threads, die aus dem ASP.NET Prozess heraus gestartet wurden, nicht mehr wie gewohnt funktionierten, obwohl in der Entwicklungsumgebung mal wieder alles klar war ("also, bei mir läufts...").
Impersonierung ist eine schon in Win32 bekannte Funktionalität, um den ausgeführten Kontext eines Prozess (meist kurzfristig) zu ändern. Das ist teilweise notwendig/sinnvoll, wenn der Prozess z.b. unter einem Benutzerkonto läuft, aber Zugriff auf Resourcen nehmen soll, die für dieses Konto nicht erreichbar sind, z.b. Datenbanken oder Netzwerkshares. Der Vorgang der Impersonierung ist für das System relativ aufwendig, d.h. man möchte das sicher nicht in einem Inner Loop alle 20ms einmal machen müssen. In einem solchen Fall würde mal vielleicht eine Queue aufbauen, die von einem Thread abgearbeitet wird, der vorher einmalig ein anderes Konto impersoniert. Regel: Impersonierung ist zu vermeiden (Ausnahmen bestätigen die Regel).
Der ASP.NET Prozess, bei IIS 6 im Task Manager als w3wsvc zu erkennen (unter IIS 5.1 ist das aspnet_wp, aber ich betrachte hier nur mal Windows Server 2003 also IIS 6), startet sich mit dem in der App Domain eingestellten Konto. Das ist meistens der lokale Network Service Account, der nur über sehr eingeschränkte Rechte verfügt. Wenn man hier einen eigenen Account einrichten möchte, dann sollte man zunächst das hier berücksichtigen und sicherstellen, dass der eigene Account Mitglied der IIS_WPG Gruppe ist, damit er den Dienst auch wirklich fahren darf. Ist das geschehen, läuft die Web App unter dem gewählten Account, der dann ggf. noch weitere Rechte für den Zugriff auf lokale oder externe Resourcen erhalten kann. Bekanntermassen laufen dann auch alle Threads, die wir aus unserer Web App starten, unter eben diesen Account. Wenn wir mit einem festen Connection String arbeiten, dann nutzen wir einen einzelnen Connection Pool unter ADO.NET, da für jede Kombination aus Connection String und User ein eigener Pool angelegt wird. Dazu mehr in einem späteren Eintrag. Da das ganze ohne Impersonierung auskommt ist es schön, schnell, übersichtlich.
Wozu ist dann der <identity.../> Tag überhaupt noch gut? - Dieser Tag regelt im Wesentlichen die Impersonierung, falls dann doch gewünscht. impersonate=true ohne userName und password führt dazu, dass der ASP.NET Prozess zwar mit dem Netzwerk Service Konto gestartet wird, aber dann sofort der angemeldete Benutzer impersoniert wird. D.h. auch, dass bei Nutzung der Windows Authentication (Integrated Security=true) im Connection String ein Pool pro Benutzer angelegt wird. Bei wenigen Benutzer mag das ok sein, vielleicht sogar Vorteile haben, aber bei einer normalen Web App möchte man das nicht. Das ist aber u.a. der Standardmodus für Sharepoint v2, weshalb ich mich schon sehr auf Sharepoint v3 freue. Baut man einen eigenen Thread auf, so vererbt sich die Impersonierung nicht automatisch auf den neuen Thread, sondern es kommt wieder der ursprüngliche Account des Prozesses, sprich das Network Service, zum Tragen. Das kann lustige Effekte haben, da dieses Konto meist nicht über die Rechte verfügt, die man dem Windows Benutzer zugestanden hat. Ich bin daher auch kein Freund dieser Variante, da sie viel Administrationsaufwand erfordert und die einzelnen Windows User Konten für meinen Geschmack zuviele Rechte erhalten müssen, z.b. auf lokale Verzeichnisse auf den Server und Zugang (wenigstens lesend) für den Datenbankserver.
Nutzt man userName/password, so wird wieder ein festes Konto verwendet, d.h. impersoniert. Man hat wieder einen Connection Pool und einen festen Account für den Zugriff auf lokale und externe Resourcen. Aber das Problem mit den Threads bleibt, die laufen wieder unter Network Services. Dennoch hat man hier wieder deutlich mehr direkte, zentrale Kontrolle, die nicht einem User Account zugewiesen werden muss. Trotzdem kann der Code Acess wie von Scott Guthrie hier beschrieben, sehr schön einfach geregelt werden.
Fazit: bei einfachen Web Apps ist die Bedeutung von allen diesen Einstellungen vielleicht nicht so wichtig. Aber spätestens bei einer Firmenweiten Lösung oder gar einem echten Produkt ist es essentiell wichtig, die richtigen Weichen zu stellen, um nicht später beim Deployment sagen zu müssen: "Also, bei mir geht's"...