【发布时间】:2022-10-18 14:34:33
【问题描述】:
我正在开发一个带有spring boot和spring security的rest api。
代码如下所示:
@RestController
@RequestMapping(path = "/api")
@PreAuthorize("isAuthenticated()")
public class RestController {
@GetMapping(path = "/get", produces = "application/json")
public ResponseEntity<InDto> get(
@AuthenticationPrincipal final CustomUser user) {
// ...
return ResponseEntity.ok(outDto);
}
}
public class CustomUser {
// does not inherit from UserDetails
}
public class CustomAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
@NonNull final HttpServletRequest request,
@NonNull final HttpServletResponse response,
@NonNull final FilterChain filterChain)
throws ServletException, IOException {
if (/* condition */) {
// ...
final CustomUser user = new CustomUser(/* parameters */);
final Authentication authentication =
new PreAuthenticatedAuthenticationToken(user, "", new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
我想在没有安全功能的情况下理想地对 RestController 类进行单元测试,但我不知道如何在测试期间注入特定的 CustomUser 对象。
我试图在每次测试之前手动将用户添加到安全上下文中(见下文),但在测试期间注入控制器的用户不是模拟的。
@WebMvcTest(RestController.class)
@AutoConfigureMockMvc(addFilters = false)
class RestControllerTest {
@Autowired private MockMvc mockMvc;
private CustomerUser userMock;
@BeforeEach
public void skipSecurityFilter() {
userMock = Mockito.mock(CustomUser.class);
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
final Authentication auth = new PreAuthenticatedAuthenticationToken(userMock, null, List.of());
SecurityContextHolder.getContext().setAuthentication(auth);
}
@Test
void test() {
mockMvc.perform(
MockMvcRequestBuilders.get("/api/get")
.contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk());
}
}
怎么了?如何将特定的 userMock 注入控制器进行测试?
编辑以使用 @WithMockCustomUser 进行测试
正如文档https://docs.spring.io/spring-security/reference/servlet/test/method.html#test-method-withsecuritycontext 中所建议的,我已将测试更新为:
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser {
}
@Service
public class WithMockCustomUserSecurityContextFactory
implements WithSecurityContextFactory<WithMockCustomUser> {
@Override
public SecurityContext createSecurityContext(final WithMockCustomUser customUser) {
final SecurityContext context = SecurityContextHolder.createEmptyContext();
final Authentication auth =
new PreAuthenticatedAuthenticationToken(Mockito.mock(IUser.class), null, List.of());
context.setAuthentication(auth);
return context;
}
}
@WebMvcTest(RestController.class)
@AutoConfigureMockMvc(addFilters = false)
class RestControllerTest {
@Autowired private MockMvc mockMvc;
private CustomerUser userMock;
@BeforeEach
public void skipSecurityFilter() {
userMock = Mockito.mock(CustomUser.class);
}
@Test
@WithMockCustomUser
void test() {
mockMvc.perform(
MockMvcRequestBuilders.get("/api/get")
.contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk());
}
}
但控制器中的用户对象仍然不是模拟(在工厂中创建)
【问题讨论】:
-
这回答了你的问题了吗? @WithMockUser with custom User implementation
-
感谢您的链接。非常有趣,但是在实现 @WithMockUser 时,提供给控制器的用户不是正确的
标签: java spring-boot rest spring-security spring-test-mvc