문제점
@DisplayName("게시글에 필요한 정보를 입력 후 등록을 하면 게시글이 저장된다.")
@Test
void createBoard() throws Exception {
// given
String title = "게시글제목";
String content = "게시글내용";
PostCreateRequestDto request = createPostRequest(title, content);
// when & then
mockMvc.perform(post("/api/v1/board")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
jakarta.servlet.ServletException: Request processing failed: java.lang.NullPointerException: Cannot invoke "cohttp://m.zoo.boardback.domain.auth.details.CustomUserDetails.getUsername()" because "userDetails" is null
테스트 하려는 코드 - 게시글 등록
@PostMapping("")
public ResponseEntity<Void> createBoard(
@RequestBody @Valid PostCreateRequestDto requestDto,
@AuthenticationPrincipal CustomUserDetails userDetails
) {
boardService.create(requestDto, userDetails.getUsername());
return ResponseEntity.ok().build();
}
- SecurityContext에 인증 정보가 없어 CustomUserDetails(UserDetails 상속한)에 값을 못 넣어주고 있다는 뜻
- NULLPointerException을 볼 수 있습니다.
어떻게 해야하지?
- WithMockUser를 사용하면 되는거 아닌가?.. 생각했는데 일단 Authentication 객체에는 내가 커스텀한 CustomUserDetails가 들어갑니다.
-> 다른 방법을 선택해야겠다. 라는 생각이 들었습니다.
- 위의 글을 참고하여 어노테이션을 만들어 해결할 수 있었습니다.
- 자세한 설명은 위의 블로그를 참고해주세요.!
WithAuthUser
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithAuthUserSecurityContextFactory.class)
public @interface WithAuthUser {
String email();
String role();
}
WithAuthUserSecurityContextFactory
public class WithAuthUserSecurityContextFactory implements WithSecurityContextFactory<WithAuthUser> {
@Override
public SecurityContext createSecurityContext(WithAuthUser annotation) {
String email = annotation.email();
String role = annotation.role();
CustomUserDetails user = new CustomUserDetails(User.builder().email(email)
.roles(List.of(Authority.builder().name(role).build())).build());
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(user, "", user.getAuthorities());
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(token);
return context;
}
}
- JWT 로그인 방식으로 SecurityContext에 인증정보를 넣어줄 때 이런 방식으로 넣어주었습니다.(STATELESS)
어노테이션 적용
@DisplayName("게시글에 필요한 정보를 입력 후 등록을 하면 게시글이 저장된다.")
@WithAuthUser(email = "test123@naver.com", role = "ROLE_USER")
@Test
void createBoard() throws Exception {
// given
String title = "게시글제목";
String content = "게시글내용";
PostCreateRequestDto request = createPostRequest(title, content);
// when & then
mockMvc.perform(post("/api/v1/board")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
뿌듯!