Rất nhiều điều đã thay đổi trong thế giới Mùa xuân kể từ khi câu hỏi này được trả lời. Spring đã đơn giản hóa việc có được người dùng hiện tại trong một bộ điều khiển. Đối với các loại đậu khác, Spring đã chấp nhận các đề xuất của tác giả và đơn giản hóa việc tiêm 'SecurityContextHolder'. Thêm chi tiết trong các ý kiến.
Đây là giải pháp tôi đã kết thúc với. Thay vì sử dụng SecurityContextHolder
trong bộ điều khiển của mình, tôi muốn tiêm thứ gì đó sử dụng SecurityContextHolder
dưới mui xe nhưng trừu tượng hóa lớp giống như đơn lẻ khỏi mã của tôi. Tôi đã tìm thấy không có cách nào để làm điều này ngoài việc lăn giao diện của riêng tôi, như vậy:
public interface SecurityContextFacade {
SecurityContext getContext();
void setContext(SecurityContext securityContext);
}
Bây giờ, bộ điều khiển của tôi (hoặc bất cứ POJO nào) sẽ trông như thế này:
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
}
}
Và, vì giao diện là một điểm tách rời, thử nghiệm đơn vị là đơn giản. Trong ví dụ này tôi sử dụng 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();
}
}
Giao diện mặc định của giao diện trông như thế này:
public class SecurityContextHolderFacade implements SecurityContextFacade {
public SecurityContext getContext() {
return SecurityContextHolder.getContext();
}
public void setContext(SecurityContext securityContext) {
SecurityContextHolder.setContext(securityContext);
}
}
Và cuối cùng, cấu hình Spring sản xuất trông như thế này:
<bean id="myController" class="com.foo.FooController">
...
<constructor-arg index="1">
<bean class="com.foo.SecurityContextHolderFacade">
</constructor-arg>
</bean>
Có vẻ hơi ngớ ngẩn khi Spring, một thùng chứa phụ thuộc tất cả mọi thứ, đã không cung cấp một cách để tiêm một cái gì đó tương tự. Tôi hiểu SecurityContextHolder
được thừa hưởng từ acegi, nhưng vẫn còn. Vấn đề là, chúng rất gần - nếu chỉ SecurityContextHolder
có một getter để lấy thể hiện bên dưới SecurityContextHolderStrategy
(đó là một giao diện), bạn có thể tiêm nó. Trên thực tế, tôi thậm chí đã mở một vấn đề Jira cho hiệu ứng đó.
Một điều cuối cùng - tôi đã thay đổi đáng kể câu trả lời tôi có ở đây trước đây. Kiểm tra lịch sử nếu bạn tò mò nhưng, như một đồng nghiệp đã chỉ ra cho tôi, câu trả lời trước của tôi sẽ không hoạt động trong môi trường đa luồng. Theo mặc định, bên dưới SecurityContextHolderStrategy
được sử dụng SecurityContextHolder
là một thể hiện của ThreadLocalSecurityContextHolderStrategy
, lưu trữ SecurityContext
s trong a ThreadLocal
. Do đó, không nhất thiết phải tiêm SecurityContext
trực tiếp vào hạt đậu vào thời điểm khởi tạo - có thể cần phải lấy ra ThreadLocal
mỗi lần, trong một môi trường đa luồng, do đó, một cái đúng được lấy ra.