게시글
질문&답변
2020.09.02
SpringSecurity의 loadUserbyUsername을 구현할때 질문이 있습니다.
리소스 서버에서 로그인 페이지를 front서버로 리다이렉트 한경우 프론트에서 email과 password의 검증을 어떤 방식으로 해야될지 고민입니다. 로그인 페이지를 커스텀하지 않았을때는 spring security에서 내부적으로 검증을 하는것이라고 생각하고 있습니다. loadUserByUsername은 email로 db에서 불러오는데 그전에 db에 있는 유저의 password와 입력한 password가 일치하는지에 대한 검증을 어떻게 해야 할까요? api 서버에 email과 password를 보내서 검증을 한뒤에 토큰을 받아와야 하는건가요? 지금 이상태로 인증서버에 email과 password를 보내면 password가 틀려도 토큰이 받아질것 같아서 질문 했습니다!!
- 0
- 2
- 608
질문&답변
2020.03.26
h2를 사용하여 test를 진행할때 한글이 들어가면 h2데이터가 깨져서 나옵니다...
선장님 해결은 했습니다. controller에서 produce부분에 produces = MediaTypes.HAL_JSON_VALUE+";charset=UTF-8" 이라고 설정해주었더니 결과가 한글이 깨지지 않습니다. 근데 애초에 그럼 MediaTyoes에 utf-8코딩이 되어있는건 없는건가요. 강의를 볼때는 쓰신 기억이 있습니다. Hateoas버젼이 올라가서 사라진건지 없는거같습니다.
- 0
- 4
- 1.6K
질문&답변
2020.03.25
h2를 사용하여 test를 진행할때 한글이 들어가면 h2데이터가 깨져서 나옵니다...
https://github.com/rlaguswhd19/Screening_Clinic RESTAPI를 복습하려고 공공데이터 포털의 선별진료소 csv파일을 사용하여 구현중에있습니다. Test를 돌릴때는 Doprint한값은 깨지지만 snippet에 한글이 제대로 들어갑니다. 하지만 mvn package를하고 snippet을 보면 한글이 깨져있습니다. print로 찍어봤을때 csv파일에서 긁은것이 한글로 잘 나옵니다.
- 0
- 4
- 1.6K
질문&답변
2020.03.17
EventControllerTest에서 여러 Test가 깨집니다. 부탁드립다..ㅠㅠ
선장님 감사합니다. ㅎㅎ
- 0
- 5
- 360
질문&답변
2020.03.17
EventControllerTest에서 여러 Test가 깨집니다. 부탁드립다..ㅠㅠ
https://github.com/rlaguswhd19/Spring_RestAPI입니다.
- 0
- 5
- 360
질문&답변
2020.03.16
EventControllerTest에서 여러 Test가 깨집니다. 부탁드립다..ㅠㅠ
package com.hj.spring.events; import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.relaxedResponseFields; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.time.LocalDateTime; import java.util.Set; import java.util.stream.IntStream; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.MediaTypes; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.security.oauth2.common.util.Jackson2JsonParser; import org.springframework.test.web.servlet.ResultActions; import com.hj.spring.accounts.Account; import com.hj.spring.accounts.AccountRepository; import com.hj.spring.accounts.AccountRole; import com.hj.spring.accounts.AccountService; import com.hj.spring.common.AppProperties; import com.hj.spring.common.BaseControllerTest; import com.hj.spring.common.TestDescription; public class EventControllerTest extends BaseControllerTest{ @Autowired private EventRepository eventRepository; @Autowired private AccountService accountService; @Autowired private AccountRepository accountRepository; @Autowired private AppProperties appProperties; @Before public void setUp() { this.accountRepository.deleteAll(); this.eventRepository.deleteAll(); } @Test @TestDescription("정상적으로 이벤트를 생성하는 테스트") public void createEvent() throws Exception { EventDto eventDto = EventDto.builder() .name("Srping") .description("Spring API Development with Spring") .beginEnrollmentDateTime(LocalDateTime.of(2020, 02, 19, 14, 19)) .closeEnrollmentDateTime(LocalDateTime.of(2020, 02, 20, 14, 19)) .beginEventDateTime(LocalDateTime.of(2020, 02, 21, 14, 19)) .endEventDateTime(LocalDateTime.of(2020, 02, 22, 14, 19)) .basePrice(100) .maxPrice(200) .limitOfEnrollment(100) .location("Daejeon") .build(); mockMvc.perform(post("/api/events/") .header(HttpHeaders.AUTHORIZATION, getBearerToken(true)) .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaTypes.HAL_JSON) .content(objectMapper.writeValueAsString(eventDto)) ) .andDo(print()) .andExpect(status().isCreated()) .andExpect(jsonPath("id").exists()) .andExpect(header().exists(HttpHeaders.LOCATION)) .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_JSON_VALUE)) .andExpect(jsonPath("free").value(false)) .andExpect(jsonPath("offline").value(true)) .andExpect(jsonPath("eventStatus").value(EventStatus.DRAFT.name())) .andExpect(jsonPath("_links.self").exists()) .andExpect(jsonPath("_links.query-events").exists()) .andExpect(jsonPath("_links.update-event").exists()) .andDo(document("create-event", links( linkWithRel("self").description("link to self"), linkWithRel("query-events").description("link to query-events"), linkWithRel("update-event").description("link to update an existing"), linkWithRel("profile").description("link to profile") ), requestHeaders( headerWithName(HttpHeaders.ACCEPT).description("accept header"), headerWithName(HttpHeaders.CONTENT_TYPE).description("content type header") ), requestFields( fieldWithPath("name").description("Name of new event"), fieldWithPath("description").description("Description of new event"), fieldWithPath("beginEnrollmentDateTime").description("BeginEnrollmentDateTime of new event"), fieldWithPath("closeEnrollmentDateTime").description("CloseEnrollmentDateTime of new event"), fieldWithPath("beginEventDateTime").description("BeginEventDateTime of new event"), fieldWithPath("endEventDateTime").description("EndEventDateTime of new event"), fieldWithPath("location").description("Location of new event"), fieldWithPath("basePrice").description("BasePrice of new event"), fieldWithPath("maxPrice").description("MaxPrice of new event"), fieldWithPath("limitOfEnrollment").description("LimitOfEnrollment of new event") ), responseHeaders( headerWithName(HttpHeaders.LOCATION).description("location header"), headerWithName(HttpHeaders.CONTENT_TYPE).description("content type header") ), //requestFields를 사용하면 links가 걸린다. 문서화하지 않았기 때문이다. 하지만 relaxed를 사용하면 모든것을 문서화하지 않아도됀다. //단점은 정확한 문서를 만들지 못한다는거다. //api가 바꼇을때 모든것을 다 하지않으면 api문서화가 제대로 되지 않는다. relaxedResponseFields( fieldWithPath("id").description("Id of new event"), fieldWithPath("name").description("Name of new event"), fieldWithPath("description").description("Description of new event"), fieldWithPath("beginEnrollmentDateTime").description("BeginEnrollmentDateTime of new event"), fieldWithPath("closeEnrollmentDateTime").description("CloseEnrollmentDateTime of new event"), fieldWithPath("beginEventDateTime").description("BeginEventDateTime of new event"), fieldWithPath("endEventDateTime").description("EndEventDateTime of new event"), fieldWithPath("location").description("Location of new event"), fieldWithPath("basePrice").description("BasePrice of new event"), fieldWithPath("maxPrice").description("MaxPrice of new event"), fieldWithPath("limitOfEnrollment").description("LimitOfEnrollment of new event"), fieldWithPath("free").description("Free of new event"), fieldWithPath("offline").description("Offline of new event"), fieldWithPath("eventStatus").description("EventStatus of new event"), fieldWithPath("_links.self.href").description("link to self"), fieldWithPath("_links.query-events.href").description("link to query-event list"), fieldWithPath("_links.update-event.href").description("link to update existing event"), fieldWithPath("_links.profile.href").description("link to profile") ) )) ; } @Test @TestDescription("입력 받을 수 없는 값을 사용한 경우에 에러가 발생하는 테스트") public void createEvent_Bad_Request_Unknown_Input() throws Exception { Event event = Event.builder() .id(100) .name("Srping") .description("REST API Development with Spring") .beginEnrollmentDateTime(LocalDateTime.of(2020, 02, 19, 14, 19)) .closeEnrollmentDateTime(LocalDateTime.of(2020, 02, 20, 14, 19)) .beginEventDateTime(LocalDateTime.of(2020, 02, 21, 14, 19)) .endEventDateTime(LocalDateTime.of(2020, 02, 22, 14, 19)) .basePrice(100) .maxPrice(200) .limitOfEnrollment(100) .location("대전 현지의 러브하우스") .eventStatus(EventStatus.PUBLISHED) .free(true) .offline(false) .build(); mockMvc.perform(post("/api/events/") .header(HttpHeaders.AUTHORIZATION, getBearerToken(true)) .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaTypes.HAL_JSON) .content(objectMapper.writeValueAsString(event)) ) .andDo(print()) .andExpect(status().isBadRequest()) ; } @Test @TestDescription("입력값이 비어있는 경우에 에러가 발생하는 테스트") public void createEvent_Bad_Request_Empty_Input() throws Exception { EventDto eventDto = EventDto.builder().build(); mockMvc.perform(post("/api/events/") .header(HttpHeaders.AUTHORIZATION, getBearerToken(true)) .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaTypes.HAL_JSON) .content(objectMapper.writeValueAsString(eventDto)) ) .andExpect(status().isBadRequest()) .andDo(print()) .andExpect(jsonPath("_links.index").exists()) ; } @Test @TestDescription("입력 값이 잘못된 경우에 에러가 발생하는 테스트") public void createEvent_Bad_Request_Wrong_Input() throws Exception { EventDto eventDto = EventDto.builder() .name("Srping") .description("REST API Development with Spring") .beginEnrollmentDateTime(LocalDateTime.of(2020, 02, 22, 14, 19)) .closeEnrollmentDateTime(LocalDateTime.of(2020, 02, 21, 14, 19)) .beginEventDateTime(LocalDateTime.of(2020, 02, 20, 14, 19)) .endEventDateTime(LocalDateTime.of(2020, 02, 19, 14, 19)) .basePrice(10000) .maxPrice(200) .limitOfEnrollment(100) .location("대전 현지의 러브하우스") .build(); mockMvc.perform(post("/api/events/") .header(HttpHeaders.AUTHORIZATION, getBearerToken(true)) .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaTypes.HAL_JSON) .content(objectMapper.writeValueAsString(eventDto)) ) .andDo(print()) .andExpect(status().isBadRequest()) .andExpect(jsonPath("content.[0].objectName").exists()) .andExpect(jsonPath("content.[0].defaultMessage").exists()) .andExpect(jsonPath("content.[0].code").exists()) .andExpect(jsonPath("_links.index").exists()) ; } @Test @TestDescription("30개의 이벤트를 10개씩 조회하는데 11~20 조회하기") public void queryEvent() throws Exception{ // Given IntStream.range(1, 31).forEach(i -> { this.generatedEvent(i); }); this.mockMvc.perform(get("/api/events/") .param("page", "1") .param("size", "10") .param("sort", "id,DESC") ) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("page").exists()) .andExpect(jsonPath("_embedded.eventList[0]._links.self").exists()) .andExpect(jsonPath("_links.self").exists()) .andExpect(jsonPath("_links.profile").exists()) .andDo(document("query-events")) ; } @Test @TestDescription("30개의 이벤트를 10개씩 조회하는데 11~20 조회하기 With Authentication") public void queryEventWithAuthentication() throws Exception{ // Given IntStream.range(1, 31).forEach(i -> { this.generatedEvent(i); }); this.mockMvc.perform(get("/api/events/") .header(HttpHeaders.AUTHORIZATION, getBearerToken(true)) .param("page", "1") .param("size", "10") .param("sort", "id,DESC") ) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("page").exists()) .andExpect(jsonPath("_embedded.eventList[0]._links.self").exists()) .andExpect(jsonPath("_links.self").exists()) .andExpect(jsonPath("_links.profile").exists()) .andExpect(jsonPath("_links.create-event").exists()) .andDo(document("query-events")) ; } @Test @TestDescription("기존의 이벤트 하나 조회하기") public void getEvent() throws Exception { // Given Account account = this.createAccount(); Event event = this.generatedEvent(100, account); // When this.mockMvc.perform(get("/api/events/"+event.getId())) .andExpect(status().isOk()) .andDo(print()) .andExpect(jsonPath("id").exists()) .andExpect(jsonPath("name").exists()) .andExpect(jsonPath("_links.self").exists()) .andExpect(jsonPath("_links.profile").exists()) .andDo(document("get-an-event")) ; } @Test @TestDescription("없는 이벤트를 조회했을때 404응답받기") public void getEvent404() throws Exception { // When this.mockMvc.perform(get("/api/events/123")) .andExpect(status().isNotFound()) ; } @Test @TestDescription("이벤트를 정상적으로 수정하기") public void updateEvent() throws Exception { // Given Account account = createAccount(); Event event = this.generatedEvent(200, account); EventDto eventDto = modelMapper.map(event, EventDto.class); String eventName = "Update event"; eventDto.setName(eventName); // When this.mockMvc.perform(put("/api/events/{id}",event.getId()) .header(HttpHeaders.AUTHORIZATION, getBearerToken(false)) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(eventDto)) .accept(MediaTypes.HAL_JSON) ) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("name").value(eventName)) .andExpect(jsonPath("_links.self").exists()) .andDo(document("update-event")) ; } @Test @TestDescription("입력값이 비어있는 경우 이벤트 수정 실패") public void updateEvent_400_Empty() throws Exception { //Given Event event = this.generatedEvent(200); EventDto eventDto = new EventDto(); this.mockMvc.perform(put("/api/events/{id}",event.getId()) .header(HttpHeaders.AUTHORIZATION, getBearerToken(true)) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(eventDto)) .accept(MediaTypes.HAL_JSON) ) .andDo(print()) .andExpect(status().isBadRequest()) ; } @Test @TestDescription("입력값이 잘못된 경우 이벤트 수정 실패") public void updateEvent_400_Wrong() throws Exception { //Given Event event = this.generatedEvent(200); EventDto eventDto = modelMapper.map(event, EventDto.class); eventDto.setBasePrice(20000); eventDto.setMaxPrice(10000); this.mockMvc.perform(put("/api/events/{id}",event.getId()) .header(HttpHeaders.AUTHORIZATION, getBearerToken(true)) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(eventDto)) .accept(MediaTypes.HAL_JSON) ) .andDo(print()) .andExpect(status().isBadRequest()) ; } @Test @TestDescription("존재하지 않는 이벤트 수정") public void updateEvent_404() throws Exception { //Given Event event = this.generatedEvent(200); EventDto eventDto = modelMapper.map(event, EventDto.class); this.mockMvc.perform(put("/api/events/12341234") .header(HttpHeaders.AUTHORIZATION, getBearerToken(true)) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(eventDto)) .accept(MediaTypes.HAL_JSON) ) .andDo(print()) .andExpect(status().isNotFound()) ; } private Event generatedEvent(int i, Account account) { Event event = BuildEvent(i); event.setManager(account); return this.eventRepository.save(event); } private Event generatedEvent(int i) { Event event = BuildEvent(i); return this.eventRepository.save(event); } private Event BuildEvent(int i) { Event event = Event.builder() .name("Srping") .description("Spring API Development with Spring") .beginEnrollmentDateTime(LocalDateTime.of(2020, 02, 19, 14, 19)) .closeEnrollmentDateTime(LocalDateTime.of(2020, 02, 20, 14, 19)) .beginEventDateTime(LocalDateTime.of(2020, 02, 21, 14, 19)) .endEventDateTime(LocalDateTime.of(2020, 02, 22, 14, 19)) .basePrice(100) .maxPrice(200) .limitOfEnrollment(100) .location("Daejeon") .free(false) .offline(true) .eventStatus(EventStatus.DRAFT) .build(); return event; } private String getBearerToken(boolean needToCreateAccount) throws Exception { return "Bearer " + getAcessToken(needToCreateAccount); } private Account createAccount() { Account khj = Account.builder() .email(appProperties.getAdminUsername()) .password(appProperties.getAdminPassword()) .roles(Set.of(AccountRole.ADMIN, AccountRole.USER)) .build(); return this.accountService.saveAccount(khj); } private String getAcessToken(boolean needToCreateAccount) throws Exception { if(needToCreateAccount) { createAccount(); } ResultActions perform = this.mockMvc.perform(post("/oauth/token") .with(httpBasic(appProperties.getClientId(), appProperties.getClientSecret())) .param("username", appProperties.getAdminUsername()) .param("password", appProperties.getAdminPassword()) .param("grant_type", "password") ) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("access_token").exists()) ; var responseBody = perform.andReturn().getResponse().getContentAsString(); Jackson2JsonParser parser = new Jackson2JsonParser(); return parser.parseMap(responseBody).get("access_token").toString(); } }
- 0
- 5
- 360
질문&답변
2020.02.25
JunitParams 관련 질문 드립니다.
선장님 감사합니다. 해결했습니다. 앞으로도 항상 좋은 강의 부탁드립니다!
- 3
- 3
- 6.4K