Я совершенно новичок в экосистеме Spring в целом и Webflux.Есть 2 вещи, которые я пытаюсь выяснить, и не могу найти какие-либо подробности о них.
Мои настройки:
Я пишу REST API Spring Boot 2 с использованием WebFlux(не используя контроллеры, а скорее функции-обработчики).Сервер аутентификации - это отдельная служба, которая выдает токены JWT, которые подключаются к каждому запросу в качестве заголовков аутентификации.Вот простой пример метода запроса:
public Mono<ServerResponse> all(ServerRequest serverRequest) {
return principal(serverRequest).flatMap(principal ->
ReactiveResponses.listResponse(this.projectService.all(principal)));
}
, который я использую, чтобы реагировать на запрос GET для получения списка всех «проектов», к которым у пользователя есть доступ.
После этого у меня есть служба, которая получает список проектов для этого пользователя, и я отправляю ответ json.
Проблемы:
Сейчас вЧтобы отфильтровать проекты на основе текущего идентификатора пользователя, мне нужно прочитать его из субъекта запроса.Одна из проблем здесь заключается в том, что у меня есть множество методов обслуживания, которым нужна информация о текущем пользователе, и передача ее в службу выглядит излишним.Одним из решений является чтение принципала внутри службы из:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Вопрос 1:
Это хорошая практика в целом, когданаписание функционального кода (если я делаю это вместо распространения принципала)?Является ли это хорошим подходом, несмотря на сложность чтения и отправки принципала из запроса в службу в каждом методе?
Вопрос 2:
Должен ли я вместо этого использоватьSecurityContextHolder Thread Local для извлечения принципала, и если я делаю это, как мне писать тесты для моей службы?
Если я использую контекст безопасности, как я проверяю реализации моей службы, которые ожидают, что принципал имеет типJWTAuthenticationToken
и я всегда получаю null
при попытке сделать что-то, как описано здесь: Юнит-тестирование с Spring Security
В тестах службы, в тестах, что мне удалось сделать до сих пор, это распространить принципал на методы службы и использовать mockito для насмешки принципала.Это довольно просто.В тестах конечных точек я использую @WithMockUser
для заполнения принципала при выполнении запросов и макетирую уровень обслуживания.Это имеет обратную сторону того, что основной тип отличается.
Вот как выглядит мой тестовый класс для сервисного уровня:
@DataMongoTest
@Import({ProjectServiceImpl.class})
class ProjectServiceImplTest extends BaseServiceTest {
@Autowired
ProjectServiceImpl projectService;
@Autowired
ProjectRepository projectRepository;
@Mock
Principal principal;
@Mock
Principal principal2;
@BeforeEach
void setUp() {
initMocks(this);
when(principal.getName()).thenReturn("uuid");
when(principal2.getName()).thenReturn("uuid2");
}
// Cleaned for brevity
@Test
public void all_returnsOnlyOwnedProjects() {
Flux<Project> saved = projectRepository.saveAll(
Flux.just(
new Project(null, "First", "uuid"),
new Project(null, "Second", "uuid2"),
new Project(null, "Third", "uuid3")
)
);
Flux<Project> all = projectService.all(principal2);
Flux<Project> composite = saved.thenMany(all);
StepVerifier
.create(composite)
.consumeNextWith(project -> {
assertThat(project.getOwnerUserId()).isEqualTo("uuid2");
})
.verifyComplete();
}
}