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 etwas SecurityContextHolder
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:
public interface SecurityContextFacade {
SecurityContext getContext();
void setContext(SecurityContext securityContext);
}
Nun würde mein Controller (oder was auch immer POJO) so aussehen:
public class FooController {
private final SecurityContextFacade securityContextFacade;
public FooController(SecurityContextFacade securityContextFacade) {
this.securityContextFacade = securityContextFacade;
}
public void doSomething(){
SecurityContext context = securityContextFacade.getContext();
// do something w/ context
}
}
Und da die Schnittstelle ein Entkopplungspunkt ist, ist das Testen von Einheiten unkompliziert. In diesem Beispiel verwende ich Mockito:
public class FooControllerTest {
private FooController controller;
private SecurityContextFacade mockSecurityContextFacade;
private SecurityContext mockSecurityContext;
@Before
public void setUp() throws Exception {
mockSecurityContextFacade = mock(SecurityContextFacade.class);
mockSecurityContext = mock(SecurityContext.class);
stub(mockSecurityContextFacade.getContext()).toReturn(mockSecurityContext);
controller = new FooController(mockSecurityContextFacade);
}
@Test
public void testDoSomething() {
controller.doSomething();
verify(mockSecurityContextFacade).getContext();
}
}
Die Standardimplementierung der Schnittstelle sieht folgendermaßen aus:
public class SecurityContextHolderFacade implements SecurityContextFacade {
public SecurityContext getContext() {
return SecurityContextHolder.getContext();
}
public void setContext(SecurityContext securityContext) {
SecurityContextHolder.setContext(securityContext);
}
}
Und schließlich sieht die Produktions-Spring-Konfiguration folgendermaßen aus:
<bean id="myController" class="com.foo.FooController">
...
<constructor-arg index="1">
<bean class="com.foo.SecurityContextHolderFacade">
</constructor-arg>
</bean>
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 nur SecurityContextHolder
einen Getter hätten, um die zugrunde liegende SecurityContextHolderStrategy
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
gebrauchte SecurityContextHolder
ist standardmäßig eine Instanz ThreadLocalSecurityContextHolderStrategy
, die speichert SecurityContext
s in ein ThreadLocal
. Daher ist es nicht unbedingt eine gute Idee, das SecurityContext
Bean zum Zeitpunkt der Initialisierung direkt in eine Bean zu injizieren. Möglicherweise muss es ThreadLocal
jedes Mal in einer Umgebung mit mehreren Threads abgerufen werden, damit das richtige abgerufen wird.