【发布时间】:2019-11-02 09:01:19
【问题描述】:
我对 Spring 生态系统和 Webflux 很陌生。有两件事我想弄清楚,但找不到任何细节。
我的设置:
我正在使用 WebFlux 编写 Spring Boot 2 REST API(不使用控制器,而是使用处理函数)。身份验证服务器是一个单独的服务,它发出 JWT 令牌,这些令牌作为身份验证标头附加到每个请求。下面是一个简单的请求方法示例:
public Mono<ServerResponse> all(ServerRequest serverRequest) {
return principal(serverRequest).flatMap(principal ->
ReactiveResponses.listResponse(this.projectService.all(principal)));
}
我用它来响应用户有权访问的所有“项目”列表的 GET 请求。
之后我有一个服务来检索该用户的项目列表并呈现一个 json 响应。
问题:
现在为了根据当前用户 ID 过滤项目,我需要从请求主体中读取它。这里的一个问题是我有很多需要当前用户信息的服务方法,并将其传递给服务似乎有点矫枉过正。一种解决方案是从以下位置读取服务内的主体:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
问题 1:
在编写功能代码时,这通常是一个好习惯吗(如果我这样做而不是传播主体)?尽管在每种方法中从请求读取主体并将其发送到服务很复杂,但这是一种好方法吗?
问题 2:
我是否应该改用 SecurityContextHolder Thread Local 来获取主体,如果这样做,我该如何为我的服务编写测试?
如果我使用安全上下文,我该如何测试我的服务实现,它需要一个类型为 JWTAuthenticationToken 的主体
当我尝试做类似这里描述的事情时,我总是得到null:Unit testing with 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();
}
}
【问题讨论】:
标签: spring-boot spring-security spring-security-oauth2 spring-webflux