Skip to content

[feat/#9] 이메일 비밀번호 인증 필터 및 인증 provider 구현#10

Merged
sejoon00 merged 5 commits intodevelopfrom
feature/#9
Jan 15, 2025
Merged

[feat/#9] 이메일 비밀번호 인증 필터 및 인증 provider 구현#10
sejoon00 merged 5 commits intodevelopfrom
feature/#9

Conversation

@sejoon00
Copy link
Contributor

@sejoon00 sejoon00 commented Jan 14, 2025

🌱 관련 이슈

📌 작업 내용 및 특이사항

Spring Security의 filter단에서 아이디 비밀번호 인증을 구현하였습니다.

로그인 요청 방식에 따른 보안 고민이 있었는데요.

생각되는 로그인 인증 방식에는 2가지가 있었습니다.

  1. 로그인 Service를 만들기

    1. login 서비스에서 아이디와 비밀번호를 dto로 받음
    2. 아이디에 해당하는 계정을 db에서 가져옴
    3. 비밀번호 체크를 하고 맞으면 token create 아니면 에러
  2. Spring Security Filter에서 인증하기
    Dispatcher Servlet 안에 들어가기 전에 filter에서 인증처리하기

[결론]
인증되지 않은 사용자의 요청이라면 어떤 악의적 의도가 있을 수 있음.

비지니스 로직들이 처리되는 스프링 컨테이너에 들이오기 전에 검증하고 차단하는 것이 바람직함.

근데 회원가입은 어쩔 수 없이 인증없이 받아야되는거 아닌가?

따라서 회원가입은 아래와 같은 추가 보안을 고려해야함
a. 입력값 검증: 회원가입 폼에서 받은 데이터를 철저히 검증해야 SQL Injection이나 XSS 같은 공격을 방지 가능
b. CSRF 보호: 만약 CSRF를 활성화한 상태라면, 회원가입 요청에서도 CSRF 토큰을 확인하도록 설정
c. Rate Limiting: 동일 IP에서 과도한 회원가입 요청을 막기 위해 Rate Limiting을 적용하면 좋음
d. 캡차(Captcha): 자동화된 봇의 악용을 방지하기 위해 회원가입 시 캡차를 도입하는 것도 좋은 방법

그럼 로그인에도 똑같이 하면 되는거 아닌가?

인증의 책임은 누가 가지는가를 고민해본다면 spring security에 인증 책임을 몰아주는 것이 맞다고 판단.

구현 난이도와 시간에 따라서 1번안을 선택할 수도 있겠지만 시간이 있으니 2번으로 구현해보자 생각했습니다.

[참고한 아키텍처]
image
참고한 아키텍처는 위와 같습니다.

추후에 로그인 상태 유지를 위해 Jwt 토큰 인증도 필요하기 때문에 저희 서비스는 1. 아이디, 비밀번호 인증, 2. jwt 인증 2가지가 필요했습니다.
인증 개수가 여러개일 때는 각 인증 구현은 AuthenticationProvider 객체가 하고 Provider 관리는 AuthenticationManager에게 위임하는 Security의 아키텍처를 사용하기로 하였습니다. 참고

하지만 참고 링크의 블로그를 보고 memberService와 Member 엔티티가 있는데 UserDetailsService나 UserDetails를 만들어야하는 것이 마음에 들지 않았습니다. 참고

image
따라서 AuthenticationProvider를 구현하는 EmailPasswordAuthenticationProvider를 만들어 UserDetailsService나 UserDetails를 사용하지않고 memberService와 Member 엔티티를 사용하도록 구현하였습니다.
그리고 이를 제외한 나머지 객체는 Spring Security가 제공하는 기본 객체를 사용하였습니다.

[전체 흐름도]
image
filter는 단순히 AuthenticationManager에게 인증할 정보를 담아 전달하고
authenticationManager는 가지고 있는 AuthenticationProvider들에게 인증을 맡기는 형태입니다.
인증이 성공하면 filter는 successHandler에게 성공처리를 맡깁니다.
인증이 실패해 예외가 발생하면 AuthenticationEntryPoint가 예외를 처리합니다.

📝 테스트 사항

  • mockMvc를 사용하여 통합 테스트로 성공, 실패 케이스 2가지를 테스트하였습니다.
  • 기존 local의 mysql db를 사용하여 테스트하던 로직에서 CI 통과를 위해 h2 DB로 변경하고 중복 되는 테스트는 삭제하였습니다.

🎯이후 추가 구현 필요한 사항

현재 서비스는 어드민 페이지와 일반 사용자 페이지가 나누어져 있어 권한에 따른 접근 제한이 필요합니다.
인증 책임은 spring security filter에서 하고 인가 책임은 인터셉터를 구현하여 권한 확인을 할 예정입니다.

PR 중 변경 사항

  1. email 같은 단순 조건으로 검색하여 예외 처리하는 로직을 service에서 repository로 이동시켰습니다.
    이유 :
    a. 해당 예외는 비즈니스 로직에 해당하는 예외라고 보지 않아도 된다고 생각하였음
    b. 단순 조회를 위한 service 계층을 하나 더 만들자니 의존성이 복잡해지고 심하면 순환 의존성까지 생길 수 있을 것 같다고 판단.

  2. 현재 예외 처리는 authenticationEntryPoint에서 일괄로 처리해주고 있는데요.
    EmailPasswordAuthenticationFilter나 Provider에서 AuthenticationException가 아닌 예외를 던지면 처리가 되지 않는 것을 알았습니다. 이를 위해 AuthenticationException를 상속하는 BadCredentialsException를 던지거나 catch해서 변환하는걸로 변경하였습니다.
    또한, 너무 많은 예외처리 정보를 주는 것도 고려하여 "잘못된 인증 정보입니다" 로 메세지는 통일하기로 하였습니다.

  3. 테스트가 너무 간단하게 작성되어 있었습니다.
    요청 본문이 다른 형식으로 왔을 때 401에러가 터지지 않았고, 유효하지 않은 형태의 이메일이 들어와도 일단 쿼리까지 나가는 문제가 있어 검증하는 로직도 추가하여 테스트했습니다.

@sejoon00 sejoon00 added Type: Test [이슈 목적] 테스트 코드 추가, 수정 For: Views [이슈 대상] views Type: Feature [이슈 목적] 새로운 기능 추가 Type: Docs/Chore [이슈 목적] 프로덕션 코드 수정 외의 문서, 패키지 매니저, 환경 수정 labels Jan 14, 2025
Copy link
Contributor

@seokbeom00 seokbeom00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다!

@sejoon00 sejoon00 merged commit 3b3ef30 into develop Jan 15, 2025
1 check passed
@sejoon00 sejoon00 self-assigned this Jan 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For: Views [이슈 대상] views Type: Docs/Chore [이슈 목적] 프로덕션 코드 수정 외의 문서, 패키지 매니저, 환경 수정 Type: Feature [이슈 목적] 새로운 기능 추가 Type: Test [이슈 목적] 테스트 코드 추가, 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ 이메일 패스워드로 로그인 인증 로직 구현

2 participants

Comments