Вступление
При использовании Spring Security для защиты наших приложений мы должны знать о его внутренней работе. Основой является Контекст безопасности , в котором хранятся данные, полученные в процессе аутентификации и необходимые для надлежащей авторизации. По определению это связано с потоком – ThreadLocal используется в качестве держателя, созданного в процессе фильтрации безопасности запроса. (читать далее) Решение, связанное с потоками, удобно, но есть один недостаток – контекст безопасности по умолчанию не распространяется на дочерние потоки. К счастью, Spring предоставляет инструменты для решения этой проблемы.
Делегирование контекста
Как указано в документации , нам предоставляется Возможность делегирования безопасности и Делегирование Контекста безопасности Исполнителю . Первый класс – это низкоуровневая оболочка для наших запускаемых экземпляров, реализованная с использованием шаблона делегирования . Он просто принимает заданный контекст и устанавливает его во время выполнения метода run() . Использование так же просто, как:
SecurityContext context = SecurityContextHolder.getContext();
DelegatingSecurityContextRunnable wrappedRunnable =
new DelegatingSecurityContextRunnable(originalRunnable, context);
Делегирование контекста безопасности исполнителю является более высокоуровневой абстракцией. Он делегирует Исполнитель экземпляры вместо Исполняемые файлы , позволяющие управлять пулами потоков с учетом контекста безопасности Spring.
В современной Java мы, скорее всего, использовали бы его с потоковым параллельным API или Завершаемое Будущее . Обе эти абстракции по умолчанию используют Java 8 по умолчанию ForkJoinPool.commonPool , что прекрасно, но обычно мы создаем пользовательские пулы, предназначенные для конкретных задач. В то время как ForkJoinPool предназначен для обработки алгоритмов “разделяй и властвуй”, крадущих работу, мы можем использовать старые добрые Исправлен пул потоков также. (читать далее)
В следующем примере показано создание пользовательского Исправлен пул потоков с Делегированием Исполнителю контекста безопасности и создание новой Завершаемой будущей задачи:
SecurityContext securityContext = SecurityContextHolder.getContext();
Executor delegatedExecutor = Executors.newFixedThreadPool(10);
Executor delegatingExecutor =
new DelegatingSecurityContextExecutor(delegatedExecutor, securityContext);
CompletableFuture.supplyAsync(() -> veryHardTask(),delegatingExecutor);
@Асинхронные методы
Приведенный выше пример показывает делегирование контекста безопасности с помощью простых параллельных методов Java. При использовании Spring мы часто используем аннотацию @Async , чтобы наши методы выполнялись асинхронно. Он использует очень собственный SimpleAsyncTaskExecutor с собственным пулом потоков. Чтобы передать наш контекст, мы могли бы создать еще одну делегацию упаковки. Однако Spring Security снова дает нам удобный способ решения проблемы :
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
Это свойство может быть настроено с помощью:
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}
Это заставляет Spring обернуть своего асинхронного исполнителя делегатом безопасности Делегирование Исполнителю задачи Контекста Безопасности . Проще говоря, мы можем безопасно использовать методы @Async, не беспокоясь о контексте безопасности.
Подведение итогов
Spring Security по определению привязана к потокам, но по умолчанию не готова к использованию в многопоточной среде. Однако с помощью простых шагов мы можем решить проблему.
Оригинал: “https://dev.to/spooz/spring-security-and-threads”