自从回答了这个问题以来,Spring 世界发生了很多变化。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
使用的底层是 的实例,它将s 存储在 a 中。因此,在初始化时直接将 bean 注入 bean 不一定是一个好主意-在多线程环境中,可能需要每次都从 bean 中检索它,因此检索到正确的。SecurityContextHolder
ThreadLocalSecurityContextHolderStrategy
SecurityContext
ThreadLocal
SecurityContext
ThreadLocal