Beaucoup de choses ont changé dans le Ressort du monde depuis que cette question a été répondu. Le printemps a simplifié l'obtention de l'utilisateur actuel dans un contrôleur. Pour d'autres haricots, le Printemps a adopté les suggestions de l'auteur et simplifiée de l'injection de "SecurityContextHolder'. Plus de détails dans les commentaires.
C'est la solution que j'ai fini par aller avec. Au lieu d'utiliser SecurityContextHolder
dans mon contrôleur, je veux injecter quelque chose qui utilise SecurityContextHolder
sous le capot, mais les résumés de suite que singleton-comme la classe de mon code. J'ai trouvé aucun moyen pour ce faire d'autre que de rouler ma propre interface, comme suit:
public interface SecurityContextFacade {
SecurityContext getContext();
void setContext(SecurityContext securityContext);
}
Maintenant, mon contrôleur (ou quel que soit POJO) devrait ressembler à ceci:
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
}
}
Et, à cause de l'interface est un point de découplage, l'unité de test est simple. Dans cet exemple, j'utilise 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();
}
}
L'implémentation par défaut de l'interface ressemble à ceci:
public class SecurityContextHolderFacade implements SecurityContextFacade {
public SecurityContext getContext() {
return SecurityContextHolder.getContext();
}
public void setContext(SecurityContext securityContext) {
SecurityContextHolder.setContext(securityContext);
}
}
Et, enfin, la production de Printemps de config ressemble à ceci:
<bean id="myController" class="com.foo.FooController">
...
<constructor-arg index="1">
<bean class="com.foo.SecurityContextHolderFacade">
</constructor-arg>
</bean>
Il semble de plus qu'un peu idiot que le Printemps, un conteneur d'injection de dépendances de toutes choses, n'a pas fourni un moyen d'injecter quelque chose de similaire. Je comprends SecurityContextHolder
a été hérité d'acegi, mais tout de même. La chose est, ils sont si proches - si seulement SecurityContextHolder
avaient un getter pour obtenir le sous-jacent SecurityContextHolderStrategy
instance (qui est une interface), vous pouvez injecter. En fait, j'ai même ouvert un Jira problème à cet effet.
Une dernière chose: j'ai juste changé de façon substantielle la réponse que j'ai eu ici avant. Vérifier l'historique si vous êtes curieux, mais, comme un collègue me l'a fait remarquer, ma précédente réponse ne serait pas travailler dans un environnement multi-thread. Le sous-jacent SecurityContextHolderStrategy
utilisée par SecurityContextHolder
est, par défaut, une instance de ThreadLocalSecurityContextHolderStrategy
, qui stocke SecurityContext
s en ThreadLocal
. Par conséquent, il n'est pas forcément une bonne idée d'injecter de l' SecurityContext
directement d'une fève à l'initialisation de temps, il faudra peut-être récupéré à partir de l' ThreadLocal
chaque fois que, dans un environnement multi-thread, donc la bonne l'un est extrait.