Mein Unternehmen hat Spring MVC evaluiert, um festzustellen, ob wir es in einem unserer nächsten Projekte verwenden sollten. Bisher liebe ich das, was ich gesehen habe, und im Moment schaue ich mir das Spring Security-Modul an, um festzustellen, ob es etwas ist, das wir verwenden können / sollten.
Unsere Sicherheitsanforderungen sind ziemlich einfach. Ein Benutzer muss lediglich einen Benutzernamen und ein Kennwort angeben können, um auf bestimmte Teile der Website zugreifen zu können (z. B. um Informationen über sein Konto zu erhalten). und es gibt eine Handvoll Seiten auf der Website (FAQs, Support usw.), auf die ein anonymer Benutzer Zugriff erhalten sollte.
In dem von mir erstellten Prototyp habe ich ein "LoginCredentials" -Objekt (das nur Benutzername und Kennwort enthält) in Session für einen authentifizierten Benutzer gespeichert. Einige der Controller überprüfen, ob sich dieses Objekt in der Sitzung befindet, um beispielsweise einen Verweis auf den angemeldeten Benutzernamen zu erhalten. Ich möchte stattdessen diese selbst entwickelte Logik durch Spring Security ersetzen, was den netten Vorteil hätte, jede Art von "Wie verfolgen wir angemeldete Benutzer?" Zu entfernen. und "Wie authentifizieren wir Benutzer?" von meinem Controller / Business Code.
Es scheint, dass Spring Security ein (pro Thread) "Kontext" -Objekt bereitstellt, um von überall in Ihrer App auf den Benutzernamen / die Hauptinformationen zugreifen zu können ...
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
... was sehr unfrühlinghaft erscheint, da dieses Objekt in gewisser Weise ein (globaler) Singleton ist.
Meine Frage lautet: Wenn dies die Standardmethode für den Zugriff auf Informationen über den authentifizierten Benutzer in Spring Security ist, wie wird ein Authentifizierungsobjekt in den SecurityContext eingefügt, damit es für meine Komponententests verfügbar ist, wenn für die Komponententests eine erforderlich ist authentifizierter Nutzer?
Muss ich dies in der Initialisierungsmethode jedes Testfalls verkabeln?
protected void setUp() throws Exception {
...
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(testUser.getLogin(), testUser.getPassword()));
...
}
Dies scheint zu ausführlich. Gibt es einen einfacheren Weg?
Das SecurityContextHolder
Objekt selbst wirkt sehr frühlingshaft ...
Machen Sie es einfach wie gewohnt und fügen Sie es dann mit
SecurityContextHolder.setContext()
in Ihre Testklasse ein, zum Beispiel:Regler:
Prüfung:
quelle
Authentication a
im controller hinzugefügt werden? Wie kann ich in jedem Methodenaufruf verstehen? Ist es in Ordnung, wenn "spring way" es nur hinzufügt, anstatt zu injizieren?@BeforeEach
(JUnit5) oder@Before
(JUnit 4). Gut und einfach.Ohne die Frage zu beantworten, wie Authentifizierungsobjekte erstellt und eingefügt werden, bietet Spring Security 4.0 einige willkommene Alternativen zum Testen. Die
@WithMockUser
Anmerkung ermöglicht es dem Entwickler, einen Scheinbenutzer (mit optionalen Berechtigungen, Benutzernamen, Kennwort und Rollen) auf übersichtliche Weise anzugeben:Es besteht auch die Möglichkeit,
@WithUserDetails
eineUserDetails
Rückgabe von zu emulierenUserDetailsService
, zWeitere Details finden Sie in den Kapiteln @WithMockUser und @WithUserDetails in den Spring Security-Referenzdokumenten (aus denen die obigen Beispiele kopiert wurden).
quelle
Sie haben Recht, besorgt zu sein - statische Methodenaufrufe sind besonders problematisch für Unit-Tests, da Sie Ihre Abhängigkeiten nicht einfach verspotten können. Was ich Ihnen zeigen werde, ist, wie Sie den Spring IoC-Container die Drecksarbeit für Sie erledigen lassen und Ihnen ordentlichen, testbaren Code hinterlassen. SecurityContextHolder ist eine Framework-Klasse, und obwohl es in Ordnung sein kann, dass Ihr Sicherheitscode auf niedriger Ebene daran gebunden ist, möchten Sie wahrscheinlich eine übersichtlichere Schnittstelle für Ihre UI-Komponenten (dh Controller) bereitstellen.
cliff.meyers erwähnte einen Weg, um es zu umgehen - erstellen Sie Ihren eigenen "Haupt" -Typ und fügen Sie eine Instanz in die Verbraucher ein. Das in 2.x eingeführte Spring < aop: scoped-proxy /> -Tag in Kombination mit einer Bean-Definition für den Anforderungsbereich und die Unterstützung der Factory-Methode sind möglicherweise das Ticket für den am besten lesbaren Code.
Es könnte folgendermaßen funktionieren:
Bisher nichts Kompliziertes, oder? Tatsächlich mussten Sie das meiste wahrscheinlich schon tun. Definieren Sie als Nächstes in Ihrem Bean-Kontext eine Bean mit Anforderungsbereich, die den Principal enthält:
Dank der Magie des aop: scoped-proxy-Tags wird die statische Methode getUserDetails jedes Mal aufgerufen, wenn eine neue HTTP-Anforderung eingeht, und alle Verweise auf die Eigenschaft currentUser werden korrekt aufgelöst. Jetzt wird das Testen von Einheiten trivial:
Hoffe das hilft!
quelle
Persönlich würde ich Powermock zusammen mit Mockito oder Easymock verwenden, um den statischen SecurityContextHolder.getSecurityContext () in Ihrem Unit- / Integrationstest zu verspotten, z
Zugegeben, hier gibt es eine Menge Code für die Kesselplatte, z. B. ein Authentifizierungsobjekt verspotten, einen SecurityContext verspotten, um die Authentifizierung zurückzugeben, und schließlich den SecurityContextHolder verspotten, um den SecurityContext zu erhalten. Er ist jedoch sehr flexibel und ermöglicht es Ihnen, Unit-Tests für Szenarien wie Null-Authentifizierungsobjekte durchzuführen usw., ohne dass Sie Ihren (Nicht-Test-) Code ändern müssen
quelle
Die Verwendung einer statischen Aufladung ist in diesem Fall der beste Weg, um sicheren Code zu schreiben.
Ja, Statik ist im Allgemeinen schlecht - im Allgemeinen, aber in diesem Fall ist die Statik genau das, was Sie wollen. Da der Sicherheitskontext einen Principal mit dem aktuell ausgeführten Thread verknüpft, würde der sicherste Code so direkt wie möglich vom Thread auf die Statik zugreifen. Durch das Ausblenden des Zugriffs hinter einer injizierten Wrapper-Klasse erhält ein Angreifer mehr Angriffspunkte. Sie benötigen keinen Zugriff auf den Code (den sie nur schwer ändern können, wenn das JAR signiert wird). Sie benötigen lediglich eine Möglichkeit, die Konfiguration zu überschreiben. Dies kann zur Laufzeit erfolgen oder indem XML in den Klassenpfad verschoben wird. Selbst die Verwendung von Annotation Injection wäre mit externem XML überschreibbar. Solches XML könnte dem laufenden System einen Rogue-Principal hinzufügen.
quelle
Ich fragte die gleiche Frage mich über hier , und habe gerade gebucht eine Antwort , dass ich vor kurzem gefunden. Die kurze Antwort lautet: Injizieren Sie a
SecurityContext
und beziehen Sie sichSecurityContextHolder
nur auf Ihre Spring-Konfiguration, um die zu erhaltenSecurityContext
quelle
Allgemeines
In der Zwischenzeit (seit Version 3.2 im Jahr 2013 dank SEC-2298 ) kann die Authentifizierung mithilfe der Annotation @AuthenticationPrincipal in MVC-Methoden eingefügt werden :
Tests
In Ihrem Unit-Test können Sie diese Methode natürlich direkt aufrufen. In Integrationstests mit können
org.springframework.test.web.servlet.MockMvc
Sieorg.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user()
den Benutzer folgendermaßen injizieren:Dies füllt jedoch nur direkt den SecurityContext aus. Wenn Sie sicherstellen möchten, dass der Benutzer aus einer Sitzung in Ihrem Test geladen wird, können Sie Folgendes verwenden:
quelle
Ich würde einen Blick auf die abstrakten Testklassen und Scheinobjekte von Spring werfen, über die hier gesprochen wird . Sie bieten eine leistungsstarke Möglichkeit zur automatischen Verkabelung Ihrer von Spring verwalteten Objekte, wodurch das Testen von Einheiten und Integrationen vereinfacht wird.
quelle
Die Authentifizierung ist eine Eigenschaft eines Threads in einer Serverumgebung, genauso wie sie eine Eigenschaft eines Prozesses in einem Betriebssystem ist. Eine Bean-Instanz für den Zugriff auf Authentifizierungsinformationen wäre unpraktisch für die Konfiguration und den Verkabelungsaufwand, ohne dass dies von Vorteil wäre.
In Bezug auf die Testauthentifizierung gibt es verschiedene Möglichkeiten, wie Sie Ihr Leben einfacher gestalten können. Mein Favorit ist es, einen benutzerdefinierten Annotations-
@Authenticated
und Testausführungs-Listener zu erstellen, der ihn verwaltet. Suchen SieDirtiesContextTestExecutionListener
nach Inspiration.quelle
Nach ziemlich viel Arbeit konnte ich das gewünschte Verhalten reproduzieren. Ich hatte den Login über MockMvc emuliert. Es ist zu schwer für die meisten Unit-Tests, aber hilfreich für Integrationstests.
Natürlich bin ich bereit, diese neuen Funktionen in Spring Security 4.0 zu sehen, die unsere Tests erleichtern.
quelle