この質問が答えられて以来、春の世界では多くの変化がありました。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
基盤となるSecurityContextHolderStrategy
インスタンス(インターフェース)を取得するためのゲッターさえあれば、それを注入できます。実際、私はそのためにJiraの問題さえも開きました。
最後にもう1つ、これまでの答えを大幅に変更しました。興味があれば履歴を確認してください。ただし、同僚が私に指摘したように、私の以前の回答はマルチスレッド環境では機能しません。SecurityContextHolderStrategy
によって使用される基になるものSecurityContextHolder
は、デフォルトではのインスタンスでありThreadLocalSecurityContextHolderStrategy
、SecurityContext
s をに格納しThreadLocal
ます。したがって、SecurityContext
初期化時にを直接Bean に注入することは必ずしも良い考えではありません。ThreadLocal
マルチスレッド環境では毎回から取得する必要がある場合があるため、正しいものが取得されます。