У весняному світі багато що змінилося з того часу, коли на це питання було дано відповідь. 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 Spring виглядає приблизно так:
<bean id="myController" class="com.foo.FooController">
...
<constructor-arg index="1">
<bean class="com.foo.SecurityContextHolderFacade">
</constructor-arg>
</bean>
Здається більш ніж трохи нерозумно, що Spring, контейнер для ін'єкцій залежностей від усіх речей, не запропонував способу ввести щось подібне. Я розумію, що SecurityContextHolder
він успадкував від ацегі, але все ж. Річ у тім, що вони настільки близькі - якби тільки SecurityContextHolder
дістав, щоб отримати базовий SecurityContextHolderStrategy
екземпляр (який є інтерфейсом), ви могли б ввести це. Насправді я навіть відкрив питання про Джиру для цього.
Останнє - я лише суттєво змінив відповідь, яку мав тут раніше. Перевірте історію, якщо вам цікаво, але, як зазначив мені колега, моя попередня відповідь не працювала б у багатопотоковому середовищі. За замовчуванням SecurityContextHolderStrategy
використовується базовий SecurityContextHolder
примірник ThreadLocalSecurityContextHolderStrategy
, який зберігає SecurityContext
s в a ThreadLocal
. Тому не обов’язково добре вводити SecurityContext
безпосередньо в квасолі під час ініціалізації - можливо, її потрібно буде отримувати ThreadLocal
кожен раз у багатопотоковому середовищі, щоб отримати правильний.