-
로그인 기능을 만들어보자프로젝트/게시판 프로젝트 2022. 5. 18. 01:23
로그인 기능을 만들기 위해 궁금한 점을 하나씩 해결해보겠습니다.
1. 로그인 방식은 POST일까 GET일까?
로그인은 사용자의 정보를 조회하는 것이니까 GET을 써야 하지 않을까?라고 생각했습니다.
하지만 GET 방식의 경우 입력받은 정보들을 URL 쿼리문으로 보내며 POST 방식은 body로 감싸서 보냅니다.
즉, GET의 경우에는 그대로 노출되고 POST의 경우에는 보안성이 증가됩니다.
로그인의 경우에는 민감한 정보로 보안이 필요하기 때문에 POST를 사용합니다.
하지만 만약 SSL을 사용한다면 GET/ POST둘다 암호화되기 때문에 GET을 사용하면 될 것 같다고 생각했습니다.
하지만 로그인하는 과정을 통해서 토큰 / 세션이 생성된다면 이는 POST가 또 맞다고 볼 수 있습니다.
또한 사용자가 URL을 복사하거나 화면캡쳐등을 사용하여 정보가 노출될 수 있으므로 일반적으로 POST를 사용하는 것이 맞는 것 같습니다.
결론 POST를 사용하자
2. 사용자가 입력한 값을 컨트롤러에 어떻게 받아와야 할까?
컨트롤러에 인자값으로 받을 수 있는 다양한 방법들이 존재합니다.
https://junuuu.tistory.com/283?category=997278
@ModelAttribute와 @RequestBody 2가지 선택지 중에서 고민을 하였습니다.
@ModelAttribute는 파라미터 값으로 DTO객체에 바인딩을 하는 방식이기 때문에 바인딩하려는 DTO객체에 Setter메서드가 반드시 있어야 합니다.
@RequestBody는 요청 본문의 body에 json이나 xml값으로 요청을 하여 HttpMessageConveter를 반드시 거쳐 DTO객체에 맞는 타입으로 바꿔서 바인딩을 시켜준다.
결론 여기서는 추가적인 Setter메서드가 필요 없는 ReqeustBody를 통해 받도록 하겠습니다.
3. 컨트롤러에서 어떤 상태코드를 반환해야 할까?
요청이 정상적으로 처리되었을때는 200 OK 상태 코드를 반환합니다.
만약 계정의 비밀번호나 아이디가 틀려서 로그인이 불가능할 경우에는 인증이 되지 않은 401 상태 코드를 반환합니다.
결론 정상 요청에는 200 OK , 인증이 되지 않았을 때는 401 Unauthorized를 반환하자
Spring Security의 경우에는 이전 페이지로 redirect를 할 수 있다고도 합니다.
@PostMapping("/login") public ResponseEntity<String> loginMember(@RequestBody MemberLoginRequestDTO memberLoginRequestDTO){ if(memberService.login(memberLoginRequestDTO)){ return ResponseEntity.status(HttpStatus.OK).body("로그인 완료"); } return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인 실패"); }
4. 서비스에서는 기능을 어떻게 구현해야 할까?
memberLoinRequestDTO를 만들어 id와 password를 입력받습니다.
그리고 id를 기반으로 Member를 조회하고 해당 id를 가진 Member의 비밀번호와 입력받은 password가 일치하는지를 true/ false로 반환합니다.
@Override public boolean login(MemberLoginRequestDTO memberLoginRequestDTO) { Optional<Member> userId = memberRepository.findByUserId(memberLoginRequestDTO.getUserId()); //Optional 사용했으니 isPresent 대신 userId.orElseThrow()를 쓰는게 좋아보인다 if(!userId.isPresent()){ return false; } return userId.get().getPassword().equals(memberLoginRequestDTO.getPassword()); }
5. 테스트
컨트롤러 테스트
@Test @DisplayName("로그인 성공시 200 상태코드 반환") public void memberLoginSuccessTest() throws Exception { //given MemberLoginRequestDTO memberLoginRequestDTO = MemberLoginRequestDTO.builder().userId("test").password("123456789").build(); String body = (new ObjectMapper()).writeValueAsString(memberLoginRequestDTO); boolean loginResult = true; given(memberService.login(any())).willReturn(loginResult); //when ResultActions resultActions = mvc.perform(post("/members/login") .content(body) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) ); //then resultActions .andExpect(status().isOk()); } @Test @DisplayName("로그인 실패시 409 상태코드 반환") public void memberLoginFailTest() throws Exception { //given MemberLoginRequestDTO memberLoginRequestDTO = MemberLoginRequestDTO.builder().userId("test").password("123456789").build(); String body = (new ObjectMapper()).writeValueAsString(memberLoginRequestDTO); boolean loginResult = false; given(memberService.login(any())).willReturn(loginResult); //when ResultActions resultActions = mvc.perform(post("/members/login") .content(body) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) ); //then resultActions .andExpect(status().isUnauthorized()); }
서비스 테스트
@Test @DisplayName("회원 로그인 성공") public void memberLoginSuccessTest(){ //given Member member = Member.builder().userId("Test").name("Test").nickName("Test").password("Test").phoneNumber("01012345678").address(new Address("a1", "a2", "a3")).build(); memberRepository.save(member); //when MemberLoginRequestDTO memberLoginRequestDTO = MemberLoginRequestDTO.builder().userId("Test").password("Test").build(); boolean result = memberService.login(memberLoginRequestDTO); //then assertThat(result).isEqualTo(true); } @Test @DisplayName("회원 로그인 실패") public void memberLoginFailTest(){ //given Member member = Member.builder().userId("Test").name("Test").nickName("Test").password("Test").phoneNumber("01012345678").address(new Address("a1", "a2", "a3")).build(); memberRepository.save(member); //when MemberLoginRequestDTO memberLoginRequestDTO = MemberLoginRequestDTO.builder().userId("Test").password("1234").build(); boolean result = memberService.login(memberLoginRequestDTO); //then assertThat(result).isEqualTo(false); }
출처
https://stackoverflow.com/questions/43965316/for-login-get-or-post
https://stackoverflow.com/questions/6110672/correct-http-status-code-for-login-form
https://gist.github.com/subicura/8329759
'프로젝트 > 게시판 프로젝트' 카테고리의 다른 글
비밀번호를 암호화 하자! (0) 2022.05.19 intellij 자동 포맷팅 시 줄 바꿈 처리 적용해주기 (0) 2022.05.19 JPA로 Update를 해보자! (0) 2022.05.14 컨트롤러를 테스트해보자! (0) 2022.05.13 [에러 해결 완료] Type definition error , InvalidDefinitionException (0) 2022.05.12