Ich habe eine Spring MVC-Webanwendung, die Spring Security verwendet. Ich möchte den Benutzernamen des aktuell angemeldeten Benutzers wissen. Ich verwende das unten angegebene Code-Snippet. Ist das der akzeptierte Weg?
Ich mag es nicht, eine statische Methode in diesem Controller aufzurufen - das macht den ganzen Zweck von Spring zunichte, IMHO. Gibt es eine Möglichkeit, die App so zu konfigurieren, dass stattdessen der aktuelle SecurityContext oder die aktuelle Authentifizierung injiziert wird?
@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(final HttpServletRequest request...) {
final String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
...
}
java
spring
spring-mvc
spring-security
Scott Bale
quelle
quelle
Antworten:
Wenn Sie Spring 3 verwenden , ist der einfachste Weg:
quelle
Seit der Beantwortung dieser Frage hat sich in der Frühlingswelt viel verändert. Spring hat es vereinfacht, den aktuellen Benutzer in eine Steuerung zu integrieren. Für andere Bohnen hat Spring die Vorschläge des Autors übernommen und die Injektion von 'SecurityContextHolder' vereinfacht. Weitere Details finden Sie in den Kommentaren.
Dies ist die Lösung, mit der ich mich am Ende entschieden habe. Anstatt
SecurityContextHolder
in meinem Controller zu verwenden, möchte ich etwasSecurityContextHolder
einfügen , das unter der Haube verwendet wird, aber diese singletonähnliche Klasse von meinem Code abstrahiert. Ich habe keine andere Möglichkeit gefunden, als meine eigene Benutzeroberfläche zu rollen:Nun würde mein Controller (oder was auch immer POJO) so aussehen:
Und da die Schnittstelle ein Entkopplungspunkt ist, ist das Testen von Einheiten unkompliziert. In diesem Beispiel verwende ich Mockito:
Die Standardimplementierung der Schnittstelle sieht folgendermaßen aus:
Und schließlich sieht die Produktions-Spring-Konfiguration folgendermaßen aus:
Es scheint mehr als ein bisschen albern, dass Spring, ausgerechnet ein Container für Abhängigkeitsinjektionen, keine Möglichkeit bietet, etwas Ähnliches zu injizieren. Ich verstehe,
SecurityContextHolder
wurde von Acegi geerbt, aber immer noch. Die Sache ist, dass sie so nah beieinander sind - wenn Sie nurSecurityContextHolder
einen Getter hätten, um die zugrunde liegendeSecurityContextHolderStrategy
Instanz (die eine Schnittstelle ist) zu erhalten, könnten Sie das injizieren. Tatsächlich habe ich sogar eine entsprechende Jira-Ausgabe eröffnet .Eine letzte Sache - ich habe gerade die Antwort, die ich hier zuvor hatte, grundlegend geändert. Überprüfen Sie den Verlauf, wenn Sie neugierig sind, aber, wie ein Mitarbeiter mir sagte, würde meine vorherige Antwort in einer Multithread-Umgebung nicht funktionieren. Die zugrundeliegende
SecurityContextHolderStrategy
gebrauchteSecurityContextHolder
ist standardmäßig eine InstanzThreadLocalSecurityContextHolderStrategy
, die speichertSecurityContext
s in einThreadLocal
. Daher ist es nicht unbedingt eine gute Idee, dasSecurityContext
Bean zum Zeitpunkt der Initialisierung direkt in eine Bean zu injizieren. Möglicherweise muss esThreadLocal
jedes Mal in einer Umgebung mit mehreren Threads abgerufen werden, damit das richtige abgerufen wird.quelle
Ich bin damit einverstanden, dass das Abfragen des SecurityContext nach dem aktuellen Benutzer stinkt. Es scheint eine sehr unfruchtbare Möglichkeit zu sein, dieses Problem zu lösen.
Ich habe eine statische "Helfer" -Klasse geschrieben, um dieses Problem zu lösen. Es ist insofern schmutzig, als es eine globale und statische Methode ist, aber ich dachte mir, wenn wir irgendetwas im Zusammenhang mit Sicherheit ändern, muss ich zumindest die Details nur an einer Stelle ändern:
quelle
Damit es nur auf Ihren JSP-Seiten angezeigt wird, können Sie das Spring Security Tag Lib verwenden:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html
Um eines der Tags verwenden zu können, muss die Sicherheits-Taglib in Ihrer JSP deklariert sein:
Dann machen Sie auf einer JSP-Seite so etwas:
HINWEIS: Wie in den Kommentaren von @ SBerg413 erwähnt, müssen Sie hinzufügen
auf das "http" -Tag in der security.xml-Konfiguration, damit dies funktioniert.
quelle
Wenn Sie Spring Security Version> = 3.2 verwenden, können Sie die
@AuthenticationPrincipal
Anmerkung verwenden:Hier
CustomUser
ist ein benutzerdefiniertes Objekt, das implementiert undUserDetails
von einem benutzerdefinierten Objekt zurückgegeben wirdUserDetailsService
.Weitere Informationen finden Sie im Kapitel @AuthenticationPrincipal der Spring Security-Referenzdokumente.
quelle
Ich erhalte einen authentifizierten Benutzer durch HttpServletRequest.getUserPrincipal ();
Beispiel:
quelle
null
wenn der Benutzer anonym authentifiziert ist (http
>anonymous
Elemente in Spring Security XML).SecurityContextHolder
oderSecurityContextHolderStrategy
ist der richtige Weg.In Spring 3+ haben Sie folgende Möglichkeiten.
Option 1 :
Option 2 :
Option 3:
Option 4: Lust auf eins: Weitere Informationen finden Sie hier
quelle
@CurrentUser
das wie der Brauch@ActiveUser
aus Ihrem Link funktioniert .@AuthenticationPrincipal
mit einer benutzerdefinierten@CurrentUser
Anmerkung umbrochen wird. Seit 3.2 müssen wir keinen benutzerdefinierten Argumentauflöser wie in der verknüpften Antwort implementieren. Diese andere Antwort enthält mehr Details.Ja, Statik ist im Allgemeinen schlecht - im Allgemeinen, aber in diesem Fall ist die Statik der sicherste Code, den Sie schreiben können. 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 der Anmerkungsinjektion im signierten Code wäre mit externem XML überschreibbar. Solches XML könnte dem laufenden System einen Rogue-Principal hinzufügen.
quelle
Ich würde einfach das tun:
quelle
SecurityContextHolderAwareRequestFilter
die die Anforderung umschließt und diesen Aufruf durch Zugriff auf die implementiertSecurityContextHolder
.Für die letzte Spring MVC-App, die ich geschrieben habe, habe ich den SecurityContext-Halter nicht eingefügt, aber ich hatte einen Basis-Controller, für den ich zwei Dienstprogrammmethoden hatte ... isAuthenticated () & getUsername (). Intern führen sie den von Ihnen beschriebenen statischen Methodenaufruf aus.
Zumindest ist es dann nur an einer Stelle, wenn Sie später umgestalten müssen.
quelle
Sie könnten Spring AOP Ansatz verwenden. Wenn Sie beispielsweise einen Dienst haben, muss dieser den aktuellen Principal kennen. Sie können eine benutzerdefinierte Anmerkung einführen, z. B. @Principal, die angibt, dass dieser Service vom Prinzipal abhängig sein sollte.
Überprüfen Sie dann in Ihrem Rat, der meiner Meinung nach MethodBeforeAdvice erweitern muss, ob ein bestimmter Dienst über eine @ Principal-Annotation verfügt, und fügen Sie den Principal-Namen ein, oder setzen Sie ihn stattdessen auf 'ANONYMOUS'.
quelle
Das einzige Problem besteht darin, dass die Benutzer- / Principal-Bean auch nach der Authentifizierung bei Spring Security nicht im Container vorhanden ist. Daher ist es schwierig, Abhängigkeiten zu injizieren. Bevor wir Spring Security verwendet haben, haben wir eine Sitzungs-Bean mit dem aktuellen Principal erstellt, diese in einen "AuthService" eingefügt und diesen Service dann in die meisten anderen Dienste in der Anwendung eingefügt. Diese Dienste würden also einfach authService.getCurrentUser () aufrufen, um das Objekt abzurufen. Wenn Sie in Ihrem Code eine Stelle haben, an der Sie in der Sitzung einen Verweis auf denselben Principal erhalten, können Sie ihn einfach als Eigenschaft für Ihre Bean mit Sitzungsbereich festlegen.
quelle
Versuche dies
quelle
Die beste Lösung, wenn Sie Spring 3 verwenden und den authentifizierten Principal in Ihrem Controller benötigen, ist Folgendes:
quelle
Ich verwende die
@AuthenticationPrincipal
Annotation sowohl in@Controller
Klassen als auch in@ControllerAdvicer
annotierten. Ex.:Wo
UserActive
ist die Klasse i für angemeldete Benutzer Dienste nutzen, und erstreckt sich vonorg.springframework.security.core.userdetails.User
. Etwas wie:Wirklich einfach.
quelle
Definieren Sie
Principal
als Abhängigkeit in Ihrer Controller-Methode, und Spring fügt beim Aufruf den aktuell authentifizierten Benutzer in Ihre Methode ein.quelle
Ich teile gerne meine Art, Benutzerdetails auf der Freemarker-Seite zu unterstützen. Alles ist sehr einfach und funktioniert perfekt!
Sie müssen nur eine erneute Authentifizierungsanforderung auf
default-target-url
(Seite nach der Formularanmeldung) stellen. Dies ist meine Controler-Methode für diese Seite:Und das ist mein ftl-Code:
Und das war's, der Benutzername wird nach der Autorisierung auf jeder Seite angezeigt.
quelle