Banyak yang telah berubah di dunia Musim Semi sejak pertanyaan ini dijawab. Spring telah menyederhanakan mendapatkan pengguna saat ini di controller. Untuk kacang lainnya, Spring telah mengadopsi saran dari penulis dan menyederhanakan injeksi 'SecurityContextHolder'. Lebih detail ada di komentar.
Ini adalah solusi yang akhirnya saya pakai. Alih-alih menggunakan SecurityContextHolder
di controller saya, saya ingin menyuntikkan sesuatu yang menggunakan di SecurityContextHolder
bawah tenda tetapi abstrak jauh seperti kelas singleton dari kode saya. Saya tidak menemukan cara untuk melakukan ini selain memutar antarmuka saya sendiri, seperti:
public interface SecurityContextFacade {
SecurityContext getContext();
void setContext(SecurityContext securityContext);
}
Sekarang, controller saya (atau POJO apa pun) akan terlihat seperti ini:
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
}
}
Dan, karena antarmuka menjadi titik decoupling, pengujian unit mudah. Dalam contoh ini saya menggunakan 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();
}
}
Implementasi default antarmuka terlihat seperti ini:
public class SecurityContextHolderFacade implements SecurityContextFacade {
public SecurityContext getContext() {
return SecurityContextHolder.getContext();
}
public void setContext(SecurityContext securityContext) {
SecurityContextHolder.setContext(securityContext);
}
}
Dan, akhirnya, konfigurasi Spring produksi terlihat seperti ini:
<bean id="myController" class="com.foo.FooController">
...
<constructor-arg index="1">
<bean class="com.foo.SecurityContextHolderFacade">
</constructor-arg>
</bean>
Tampaknya lebih dari sedikit konyol bahwa Spring, wadah injeksi ketergantungan dari semua hal, belum menyediakan cara untuk menyuntikkan sesuatu yang serupa. Saya mengerti SecurityContextHolder
diwarisi dari acegi, tapi tetap saja. Masalahnya, mereka sangat dekat - jika hanya SecurityContextHolder
memiliki pengambil untuk mendapatkan SecurityContextHolderStrategy
contoh yang mendasarinya (yang merupakan antarmuka), Anda bisa menyuntikkan itu. Bahkan, saya bahkan membuka masalah Jira untuk efek itu.
Satu hal terakhir - Saya baru saja mengubah jawaban yang saya miliki di sini sebelumnya. Periksa riwayatnya jika Anda penasaran, tetapi, seperti yang ditunjukkan oleh seorang rekan kerja kepada saya, jawaban saya sebelumnya tidak akan berfungsi di lingkungan multi-utas. Yang mendasari yang SecurityContextHolderStrategy
digunakan oleh SecurityContextHolder
adalah, secara default, instance dari ThreadLocalSecurityContextHolderStrategy
, yang menyimpan SecurityContext
s dalam ThreadLocal
. Oleh karena itu, tidak selalu merupakan ide yang baik untuk menyuntikkan SecurityContext
langsung ke dalam kacang pada waktu inisialisasi - mungkin perlu diambil dari ThreadLocal
setiap waktu, dalam lingkungan multi-threaded, sehingga yang benar diambil.