[Security] Form Login 인증 구현하기

손은실·2024년 6월 7일
0

Spring Boot

목록 보기
9/13
post-thumbnail
post-custom-banner

본 포스팅에서는 가독성을 위해 코드를 캡쳐해 업로드 했으며, 사용된 코드는 EunsilSon/spring-security-form-login에서 확인할 수 있습니다.

개발 환경
Spring Boot 3.2.6 / Spring Security 6 / JDK17 / MySQL / Mustache

추천 레퍼런스
본 포스팅은 메타코딩 - Springboot - 시큐리티 특강을 참고하여 작성했으며, 해당 강의를 ❗강력 추천❗합니다. 강의에 나온 코드와 약 80% 동일하며, 다른 방식으로 구현한 코드가 조금 있지만 동일한 결과를 출력하는 코드입니다.
추가로, 망나니개발자 - [SpringBoot] Spring Security란?을 읽고 Spring Security의 전체적인 실행 흐름을 파악할 수 있어 실제 구현할 때 이해하는데 많은 도움이 되었습니다.



들어가며

폼 로그인에서 사용되는 주요 클래스와 전체적인 흐름은 아래와 같습니다.

  • IndexController: 페이지 이동 및 회원가입 진행
    데이터베이스에 User 정보 저장

  • PrincipalDetailService: Security가 대신 로그인을 진행

    • UserDetailService의 구현체
    • 로그인 컨트롤러를 만들지 않아도 됨
  • PrincipalDetails: Security Session 등록

    • UserDetails의 구현체
    • 로그인 성공 후에만 세션에 저장
  • IndexController: 로그인 성공 후 index.html 리다이렉트


디렉토리 구조

┌── config/  
│ ├── auth/
│ │ ├── PrincipalDetailService  
│ │ └── PrincipalDetails   
│ ├── SecurityConfig  
│ └── WebMvcConfig  
├── controller/  
│ └── IndexController  
├── model/
│ └── User  
└── repository/
  └── UserRepository  


시큐리티 기본 설정

🟢 build.gradle

implementation 'org.springframework.boot:spring-boot-starter-security'

Spring Security를 추가하고 바로 애플리케이션을 실행하면 콘솔창에 패스워드가 하나 출력됩니다.

스프링 시큐리티는 기본적으로 모든 요청에 대해 인증을 요구하기 때문에 인증되지 않은 사용자가 접근 시 ‘/login’ 경로로 리다이렉트 됩니다.

아래와 같이 폼에 입력하면 로그인이 가능합니다.

Username: user
Password: 콘솔창에 뜬 패스워드

로그인 성공 후 리다이렉션 경로를 설정해주지 않아 404 에러가 발생합니다.



웹 설정

🟢 build.gradle

Mustache는 스프링 부트에서 공식적으로 지원하는 템플릿 엔진입니다.

implementation 'org.springframework.boot:spring-boot-starter-mustache'

🟢 WebMvcConfig

템플릿의 환경을 설정해줍니다.



사용자 관련 설정

🟢 User

🟢 UserRepository

회원가입과 로그인 과정에서 필요한 JPA 메서드를 설정합니다.

  • existsBy : 엔티티 존재 여부 확인
  • findBy : 엔티티 추출


회원가입 & 로그인 구현

🟢 index.html

로그인 성공 시 이동하게 되는 인덱스 페이지입니다.

🟢 loginForm.html

로그인 페이지를 직접 만들어 줍니다. 아래에 회원가입 페이지로 연결되는 링크를 작성합니다.

🟢 joinForm.html

회원가입 페이지에서 사용자가 입력한 정보들이 IndexController의 ‘/join’ 경로에 post 메서드로 전달됩니다.

🟢 IndexController

Mustache의 기본 폴더 경로는 ‘src/main/resources’ 이므로, 경로를 임의로 변경하지 않는다면 html 파일의 이름만 반환해도 페이지 이동이 가능합니다.

회원가입 로직은 Service로 따로 빼는 것이 좋지만, 몇 줄 없어서 Controller에 적었습니다.
회원가입 후, 로그인 페이지로 리다이렉트 합니다.

  • bCryptPasswordEncoder.encode : 전달 받은 패스워드를 암호화하여 데이터베이스에 저장합니다.
    • 암호화를 하지 않고 평문 패스워드를 저장한다면 security 로그인을 사용할 수 없게 됩니다.


보안 설정 커스텀

🟢 SpringConfig

시큐리티 설정을 본인의 프로젝트 상황에 맞게 설정합니다.

  • @EnableWebSecurity : Spring Filter가 Spring Filter Chain에 등록
  • BCryptPasswordEncoder : 패스워드 암호화에 사용될 구현체를 지정합니다.
    • @Bean 어노테이션을 사용하면 해당 메서드에서 반환되는 객체를 IoC로 등록해 스프링에서 이를 관리하게 됩니다.
  • authorizeHttpRequests : 인증이 필요한 URL과 접근을 허용할 URL을 지정합니다.
  • formLogin : 폼 로그인을 위한 여러 설정
    • loginPage : 기본 URL은 ‘/login’ 이지만, 다른 url을 사용하고 싶을 때 지정합니다.
    • loginProcessingUrl : 개발자가 컨트롤러에 로그인 요청을 받을 메서드를 작성하지 않아도 security가 대신 로그인을 진행합니다.
    • defaultSuccessUrl : 로그인 성공 후 이동할 주소를 지정합니다.

🟢 PrincipleDetailService

/login 주소 요청이 왔을 때 security가 낚아채서 로그인을 진행하는 클래스입니다.

SecurityConfig에 작성한 loginProcessingUrl(”/login”) 을 통해 /login 요청이 오면 Spring Security가 자동으로 UserDetailsService 타입으로 IoC 되어 있는 loadByUsername 함수를 실행합니다.

username과 같은 엔티티가 있다면 해당 엔티티를 꺼내 반환하고, 없다면 null을 반환합니다.


🟢 PrincipleDetails

로그인에 성공한뒤 시큐리티 session을 만드는 클래스입니다.

Security Session

  • Security Context Holder : 보안 주체에 대한 세부 정보
  • Security Context : Authentication 타입의 객체를 보관하며, 객체를 꺼낼 수 있음
  • Authentication : 현재 접근하는 주체의 정보와 권한을 UserDeatils 타입으로 가짐

Security Context Holder로 Security Context에 접근하고, Security Context를 통해 Authentication에 접근해서 객체를 꺼내는데, 그 객체의 타입이 결국 UserDetails이다.

그래서 Spring Security에 세션을 저장하기 위해 UserDeatils를 구현한 클래스를 만든다…!!!



실행

모든 구현이 끝났습니다! 전체적인 실행 흐름을 콘솔로 출력한 결과입니다.

데이터베이스에도 사용자 정보가 잘 저장되었으며, 패스워드는 암호화 된 상태로 저장됩니다.



마무리

Security를 이용해 로그인을 구현한 포스팅이 많은데, 따라하다가도 전체 코드를 올린 경우가 드물고, 코드 한 줄 마다 왜 사용 했고, 어떻게 흐름이 이어지는지 알기가 어려워 security가 이렇게 어려운 건가 주눅 들었습니다...
그렇게 한동안 헤매다가 좋은 글과 강의를 발견해 드디어 이해할 수 있게 되었습니다!
본 포스팅에서 사용한 코드를 깃허브에 올려놨으니 많은 분들께 도움이 되었으면 좋겠습니다 😄

post-custom-banner

0개의 댓글