Nachdem ein neuer Benutzer ein Formular "Neues Konto" eingereicht hat, möchte ich diesen Benutzer manuell anmelden, damit er sich nicht auf der folgenden Seite anmelden muss.
Die normale Formular-Anmeldeseite, die den Spring Security Interceptor durchläuft, funktioniert einwandfrei.
Im New-Account-Form-Controller erstelle ich ein UsernamePasswordAuthenticationToken und setze es manuell im SecurityContext:
SecurityContextHolder.getContext().setAuthentication(authentication);
Auf derselben Seite überprüfe ich später, ob der Benutzer angemeldet ist mit:
SecurityContextHolder.getContext().getAuthentication().getAuthorities();
Dies gibt die Berechtigungen zurück, die ich zuvor in der Authentifizierung festgelegt habe. Alles ist gut.
Wenn jedoch derselbe Code auf der nächsten Seite aufgerufen wird, die ich lade, lautet das Authentifizierungstoken nur UserAnonymous.
Mir ist nicht klar, warum die bei der vorherigen Anforderung festgelegte Authentifizierung nicht beibehalten wurde. Irgendwelche Gedanken?
- Könnte es daran liegen, dass die Sitzungs-IDs nicht richtig eingerichtet sind?
- Gibt es etwas, das meine Authentifizierung möglicherweise irgendwie überschreibt?
- Vielleicht brauche ich nur einen weiteren Schritt, um die Authentifizierung zu speichern?
- Oder muss ich etwas tun, um die Authentifizierung während der gesamten Sitzung zu deklarieren, anstatt irgendwie eine einzelne Anfrage?
Ich suche nur nach Gedanken, die mir helfen könnten zu sehen, was hier passiert.
quelle
SecurityContextHolder.getContext().setAuthentication(authentication)
. Es funktioniert und ist üblich, aber es gibt schwerwiegende Funktionsmängel, auf die Sie stoßen werden, wenn Sie dies einfach tun. Weitere Informationen finden Sie in meiner Frage und in der Antwort: stackoverflow.com/questions/47233187/…Antworten:
Ich hatte vor einiger Zeit das gleiche Problem wie Sie. Ich kann mich nicht an die Details erinnern, aber der folgende Code hat die Dinge für mich zum Laufen gebracht. Dieser Code wird in einem Spring Webflow-Flow verwendet, daher die Klassen RequestContext und ExternalContext. Der für Sie relevanteste Teil ist jedoch die doAutoLogin-Methode.
quelle
@Configuration public class WebConfig extends WebSecurityConfigurerAdapter { @Bean @Override public AuthenticationManager authenticationProvider() throws Exception { return super.authenticationManagerBean(); } }
Ich konnte keine anderen vollständigen Lösungen finden und dachte, ich würde meine veröffentlichen. Dies mag ein bisschen hacken, aber es hat das Problem mit dem oben genannten Problem behoben:
quelle
authenticationManager
?Letztendlich die Wurzel des Problems herausgefunden.
Wenn ich den Sicherheitskontext manuell erstelle, wird kein Sitzungsobjekt erstellt. Erst wenn die Anforderung die Verarbeitung abgeschlossen hat, erkennt der Spring Security-Mechanismus, dass das Sitzungsobjekt null ist (wenn er versucht, den Sicherheitskontext in der Sitzung zu speichern, nachdem die Anforderung verarbeitet wurde).
Am Ende der Anforderung erstellt Spring Security ein neues Sitzungsobjekt und eine neue Sitzungs-ID. Diese neue Sitzungs-ID gelangt jedoch nie zum Browser, da sie am Ende der Anforderung auftritt, nachdem die Antwort an den Browser erfolgt ist. Dies führt dazu, dass die neue Sitzungs-ID (und damit der Sicherheitskontext mit meinem manuell angemeldeten Benutzer) verloren geht, wenn die nächste Anforderung die vorherige Sitzungs-ID enthält.
quelle
Aktivieren Sie die Debug-Protokollierung, um ein besseres Bild von den Vorgängen zu erhalten.
Mithilfe eines browserbasierten Debuggers können Sie feststellen, ob die Sitzungscookies gesetzt werden, um die in HTTP-Antworten zurückgegebenen Header anzuzeigen. (Es gibt auch andere Möglichkeiten.)
Eine Möglichkeit besteht darin, dass SpringSecurity sichere Sitzungscookies setzt und Ihre nächste angeforderte Seite eine "http" -URL anstelle einer "https" -URL enthält. (Der Browser sendet kein sicheres Cookie für eine "http" -URL.)
quelle
Die neue Filterfunktion in Servlet 2.4 verringert im Wesentlichen die Einschränkung, dass Filter nur im Anforderungsfluss vor und nach der eigentlichen Anforderungsverarbeitung durch den Anwendungsserver ausgeführt werden können. Stattdessen können Servlet 2.4-Filter jetzt an jedem Versandpunkt mit dem Anforderungs-Dispatcher interagieren. Dies bedeutet, dass, wenn eine Webressource eine Anforderung an eine andere Ressource weiterleitet (z. B. ein Servlet, das die Anforderung an eine JSP-Seite in derselben Anwendung weiterleitet), ein Filter ausgeführt werden kann, bevor die Anforderung von der Zielressource verarbeitet wird. Dies bedeutet auch, dass Servlet 2.4-Filter vor und nach jeder der enthaltenen Ressourcen arbeiten können, wenn eine Webressource die Ausgabe oder Funktion anderer Webressourcen enthält (z. B. eine JSP-Seite, die die Ausgabe mehrerer anderer JSP-Seiten enthält). .
Um diese Funktion zu aktivieren, benötigen Sie:
web.xml
RegistrationController
quelle
Ich habe versucht, eine extjs-Anwendung zu testen, und nachdem ich erfolgreich ein testingAuthenticationToken festgelegt hatte, funktionierte dies plötzlich ohne offensichtlichen Grund nicht mehr.
Ich konnte die oben genannten Antworten nicht zum Laufen bringen, daher bestand meine Lösung darin, diesen Frühling in der Testumgebung auszulassen. Ich habe eine Naht um den Frühling herum eingeführt:
Benutzer ist hier ein benutzerdefinierter Typ.
Ich wickle es dann in eine Klasse ein, die nur eine Option für den Testcode hat, um die Feder herauszuschalten.
Die Testversion sieht einfach so aus:
Im aufrufenden Code verwende ich immer noch einen richtigen Benutzer, der aus der Datenbank geladen wurde:
Dies ist natürlich nicht geeignet, wenn Sie die Sicherheit tatsächlich verwenden müssen, aber ich verwende ein Setup ohne Sicherheit für die Testbereitstellung. Ich dachte, jemand anderes könnte in eine ähnliche Situation geraten. Dies ist ein Muster, mit dem ich zuvor statische Abhängigkeiten verspottet habe. Die andere Alternative ist, dass Sie die Statik der Wrapper-Klasse beibehalten können, aber ich bevorzuge diese, da die Abhängigkeiten des Codes expliziter sind, da Sie CurrentUserAccessor an Klassen übergeben müssen, in denen dies erforderlich ist.
quelle