自从回答这个问题以来,春季世界发生了很多变化。Spring简化了将当前用户加入控制器的过程。对于其他bean,Spring采纳了作者的建议,并简化了“ SecurityContextHolder”的注入。更多详细信息在注释中。
这是我最终要解决的问题。而不是SecurityContextHolder
在控制器中使用,我想注入一些SecurityContextHolder
在幕后使用但从我的代码中抽象出类似单例类的东西。除了滚动自己的界面,我发现没有其他方法可以这样做,例如:
public interface SecurityContextFacade {
SecurityContext getContext();
void setContext(SecurityContext securityContext);
}
现在,我的控制器(或任何POJO)将如下所示:
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
}
}
而且,由于接口是去耦点,因此单元测试非常简单。在此示例中,我使用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();
}
}
该接口的默认实现如下所示:
public class SecurityContextHolderFacade implements SecurityContextFacade {
public SecurityContext getContext() {
return SecurityContextHolder.getContext();
}
public void setContext(SecurityContext securityContext) {
SecurityContextHolder.setContext(securityContext);
}
}
最后,生产Spring配置如下所示:
<bean id="myController" class="com.foo.FooController">
...
<constructor-arg index="1">
<bean class="com.foo.SecurityContextHolderFacade">
</constructor-arg>
</bean>
Spring,万物的依赖注入容器,似乎并没有提供一种注入类似东西的方式,这似乎有点愚蠢。我明白SecurityContextHolder
是从acegi继承而来的,但仍然如此。事实是,它们是如此接近-如果只有SecurityContextHolder
一个getter来获取基础SecurityContextHolderStrategy
实例(这是一个接口),则可以注入它。实际上,我什至为此揭开了一个Jira问题。
最后一件事-我刚刚改变了我以前在这里得到的答案。如果您好奇,请检查历史记录,但是,正如一位同事向我指出的那样,我以前的回答在多线程环境中不起作用。底层SecurityContextHolderStrategy
使用SecurityContextHolder
,默认情况下,的一个实例ThreadLocalSecurityContextHolderStrategy
,其存储SecurityContext
S IN一个ThreadLocal
。因此,不一定要SecurityContext
在初始化时将它直接注入Bean中-可能需要从Bean中检索它。ThreadLocal
在多线程环境中,每次都,以便检索正确的一个。