IT/디버깅

@WithMockUser 사용시 요청에 사용되는 User와 MockUser가 다른 경우 발생하는 에러 401

happy_life 2023. 11. 11. 15:32

이번 포스팅에서는 realUser로 테스트를 작성해야 하는데 @WithMockUser 로 Authentication을 하게 되면, 인증과 인가의 context가 달라 401 에러가 발생하는 이슈를 해결하는 과정을 설명하고 있습니다.

 

@WithMockUser 코드

@Test
@Transactional
@WithMockUser ( username = "patient", roles = { "PATIENT" } )
@DisplayName("testDeleteAppointmentRequestsAPI - 성공")
public void testDeleteAppointmentRequestsAPI () throws Exception {
    // given
    final AppointmentRequestForm appointmentForm = new AppointmentRequestForm();
    appointmentForm.setDate( "2030-11-19T04:50:00.000-05:00" ); // 2030-11-19
    // 4:50 AM
    // EST
    appointmentForm.setType( AppointmentType.GENERAL_CHECKUP.toString() );
    appointmentForm.setStatus( Status.PENDING.toString() );
    appointmentForm.setHcp( "testHcp" );
    appointmentForm.setPatient( "testPatient" );
    appointmentForm.setComments( "Test appointment please ignore" );

    // when
    mvc.perform( post( "/api/v1/appointmentrequests" ).contentType( MediaType.APPLICATION_JSON )
            .content( TestUtils.asJsonString( appointmentForm ) ) );

    User testPatient = patientService.findByName("testPatient");
    Long testArId = arService.findByPatient(testPatient).get(0).getId();

    mvc.perform( get( "/api/v1/appointmentrequests/" + testArId ) )
            .andExpect( status().isOk() )
            .andExpect( content().contentType( MediaType.APPLICATION_JSON_VALUE ) );
   
   }

 

@MockUser는 patient, 실제 사용한 user는 testPatient

 

 

의문점

authentication permit all이 되어있는데 왜 401 응답이 올까?

@PreAuthorize는 authentication과 authorization을 모두 내포한다. 따라서 authentication은 MockUser로 하고 실제 접근은 testPatient로 하므로 401 응답이 발생하는 것이다.

 

 

 

@WithMockUser 해결

1-1. login을 통해 authentication한 MockSession을 가져옵니다.

private MockHttpSession doLogin(String username, String password) throws Exception {
    MvcResult result = mvc.perform(post("/login")
                    .param("username", username)
                    .param("password", password))
            .andExpect(status().is3xxRedirection())
            .andReturn();

    return (MockHttpSession) result.getRequest().getSession();
}

 

authentication이 잘 되었는지 확인하기 위해 아래의 코드를 통해 체크할 수 있습니다.

MockHttpSession session = doLogin("testPatient", "1234");
SecurityContext securityContext = (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
Authentication authentication = securityContext.getAuthentication();

 

 

 

1-2. mockMvc의 perform에 authentication된 session을 입력합니다.

// when
mvc.perform(post("/api/v1/appointmentrequests").contentType(MediaType.APPLICATION_JSON)
                .content(TestUtils.asJsonString(appointmentForm))
                .session(session)
);

 

 

 

@WithMockUser 전체코드

@Test
@Transactional
@DisplayName("testDeleteAppointmentRequestsAPI - 성공")
public void testDeleteAppointmentRequestsAPI() throws Exception {
    MockHttpSession session = doLogin("testPatient", "1234");

    // given
    final AppointmentRequestForm appointmentForm = new AppointmentRequestForm();
    appointmentForm.setDate("2030-11-19T04:50:00.000-05:00"); // 2030-11-19
    // 4:50 AM
    // EST
    appointmentForm.setType(AppointmentType.GENERAL_CHECKUP.toString());
    appointmentForm.setStatus(Status.PENDING.toString());
    appointmentForm.setHcp("testHcp");
    appointmentForm.setPatient("testPatient");
    appointmentForm.setComments("Test appointment please ignore");

    // when
    mvc.perform(post("/api/v1/appointmentrequests").contentType(MediaType.APPLICATION_JSON)
                    .content(TestUtils.asJsonString(appointmentForm))
                    .session(session)
    );

    User testPatient = patientService.findByName("testPatient");
    Long testArId = arService.findByPatient(testPatient).get(0).getId();

    // then
    mvc.perform(delete("/api/v1/appointmentrequests/" + testArId))
            .andExpect(status().isOk());
}


private MockHttpSession doLogin(String username, String password) throws Exception {
    MvcResult result = mvc.perform(post("/login")
                    .param("username", username)
                    .param("password", password))
            .andExpect(status().is3xxRedirection())
            .andReturn();

    return (MockHttpSession) result.getRequest().getSession();
}