꿈소년의 개발 이야기

[스프링 부트 개발자 온보딩 가이드 스터디] Chapter 06 Minilog에 인증 기능 추가하기 본문

Do it 스터디!/스프링 부트 개발자 온보딩 가이드 스터디!

[스프링 부트 개발자 온보딩 가이드 스터디] Chapter 06 Minilog에 인증 기능 추가하기

fogthegreat 2026. 4. 5. 21:25
반응형

DAY 7

🔖 오늘 읽은 범위 :

🌱공부 내용: Chapter 06 Minilog에 인증 기능 추가하기
👢쪽수: p.219-p.290


  • 목표: Minilog API 에 안전한 인증 시스템 추가
  • 기능 요구사항
    • JWT(JSON Web Token)의 생성 및 검증 기능 추가.
      • JWT에 사용자의 User Name, ID, 발급 시간, 만료 시간 포함.
      • AUTHOR와 ADMIN 두 가지 권한 부여 기능 제공.
        • AUTHOR: 게시글 작성 및 조회 권한.
        • ADMIN: 게시글 관리 및 사용자 관리(수정, 삭제) 권한.
    • 각 컨트롤러의 엔드포인트에 JWT 인증 기능 추가. 즉, 인가되지 않은 사용자는 엔드포인트에 접근하지 못하도록 차단.
  • 구현 요구사항
    • 로그인 및 Swagger 페이지를 제외한 모든 엔드포인트는 인증을 필수적으로 요구.
    • User ID를 입력 받는 기존 컨트롤러 및 DTO를 수정하여, JWT에 포함된 식별자를 사용하도록 변경.

JWT 인증 이해하기

주요 인증 기법

  • 인증 Authentication: 사용자와 시스템 간 신뢰를 형성. 시스템이 사용자로부터 받은 정보를 바탕으로 요청자가 실제인지 확인하는 데 목적이 있음.

세션 기반 인증

  • 사용자가 인증에 성공 → 서버 세션 ID 생성 → 클라이언트 쿠키에 저장하는 방식.
  • 클라이언트 요청 ⇒ 세션 ID를 전송해서 인증 상태를 유지한다.
  • 장점: 구현이 간단하다. 많이 사용된다. 세션 정보가 서버에 저장되어 보안성이 높다.
  • 단점: 서버가 상태를 유지해야 하는 부담이 있다. 확장성이 낮다. 분산 시스템 환경에서는 서버 간 세션 동기화가 필요하다.

토큰 기반 인증

  • 서버가 사용자를 인증 → JWT 와 같은 토큰을 생성하여 클라이언트에 반환하는 방식.
  • 서버는 세션 정보를 유지하지 않는다.
  • 클라이언트는 발급 받은 토큰을 서버 요청에 함께 보내 인증을 수행한다.
  • 서버는 클라이언트가 요청에 같이 담아 보내온 토큰이 유효한지 검사한다. 유효하지 않으면 403 에러 등을 반환한다.
  • 장점: 여러 서버로 부하를 분산하기에도 유리하다.
  • 단점: 토큰이 유출되면 제3자가 해당 토큰을 이용해 시스템에 접근이 가능하다.

OAuth 2.0

  • 제3자 앱이 사용자의 리소스에 접근할 수 있도록 권한을 부여하는 인증 방식.
  • 사용자가 앱에 로그인 → 앱은 OAuth 제공자로부터 접근 토큰을 받음. → 사용자 리소스에 접근한다.
  • 장점: 제3자 인증 서비스와 쉽게 통합할 수 있다. 자원 소유자의 명시적인 동의가 필요하다.
  • 단점: 구현이 복잡하다. 부가적인 네트워크 호출이 필요해서 속도가 느리다.

기타 인증

  • 생체 인증, WebAuthn(FIDO2), SMS OTP(One Time Password), 이메일 OTP, SSO(Single Sign-On) 등.

Why JWT?

  • 과거에는 대부분의 웹 애플리케이션이 세션 기반 인증을 했었음.
    • 단일 서버에서는 간단하고 효과적임. 서버 인증 상태를 유지해야 해서 확장성이 낮음.
    • 분산 환경에서는 서버 간 세션 동기화가 필요해서 어려움.
    • MSA(마이크로 서비스 아키텍처) 구조에서는 불리한 방식임.
  • JWT 기반 인증
    • 서버 무상태성을 지원한다. ⇒ 인증 상태를 별도로 저장하지 않는다.
    • JWT는 사용자 ID, 역할 Role, 권한 Permission, 만료 시간 Expiration 등 다양한 정보를 JSON 포맷에 담아 둔다.
  • 표준화
    • 분산 시스템, REST API 확산
    • MSA, 서버리스 아키텍처
    • 이질적인 시스템 간 인증과 권한 관리 통합에도 적절함.
    • 인증 및 권한 부여 프레임워크들이 기본 토큰 형식으로 채택 하기도 함. OAuth 2.0, OpenID Connect
  • 한계점
    • 토큰 유출. 토큰을 안전하게 저장해야 함.
    • 요청 헤더 처리 성능에 영향을 줌. 세션 ID보다 크기가 큼.
    • 만료될 때 까지 토큰을 무효화 하기 어렵다.
  • 대응
    • HTTPS 사용.
    • 만료 시간이 짧은 Access Token, 긴 수명의 Refresh Token 을 함께 사용을 권장.
    • 필요한 최소한의 정보만 포함해야 함.

JWT 구조와 메커니즘

  • 인증과 권한 부여(Authorization)을 동시에 지원하는 무상태(Stateless) 방식의 토큰.
  • 인증 단계: 사용자 신원 확인.
  • 권한 부여 단계: 특정 리소스에 접근할 수 있는 권한인지 판단.
  • 구성요소: 헤더(Header), 페이로드(Payload), 시그니처(Signature)로 구성. Base64Url 로 인코딩 되고, 점(.) 을 기준으로 연결되어 하나의 문자열로 표현.
    • 헤더
      • 토큰의 타입, 서명 알고리즘 정보 포함.
    • 페이로드
      • 토큰 데이터 = 클레임(Claim) 으로 구성.(키 - 값 쌍)
      • 사용자 아이디, 역할, 토큰 만료 시간 등의 정보.
    • 시그니처
      • 헤더와 페이로드를 결합한 것에 대한 서명.
      • 비밀 키를 사용해 생성된 서명.
      • 토큰 무결성 보장.

JWT 인증 순서

  1. 사용자 이름 & 비밀번호 전송.
  2. 사용자 정보 확인.
  3. JWT 발급.
  4. JWT 포함 요청 전송.
  5. JWT 검증 및 권한 확인.
  6. 요청 처리 결과 반환.

스프링 시큐리티를 이용한 JWT 인증 기능 통합하기

  • 스프링 보안 프레임워크 ⇒ 스프링 시큐리티 Spring Security

스프링 시큐리티 핵심 기능

  • 인증(Authentication)
    • 사용자 신원 확인 과정.
    • 일반적으로 아이디와 비밀번호로 확인.
    • 스프링 시큐리티 기본 인증 수단으로 UsernamePasswordAuthenticationFilter제공.
  • 권한 부여(Authorization)
    • 인증된 사용자가 애플리케이션 내에서 특정 작업을 수행할 수 있는 권한을 확인한다.
    • 사용자 역할 및 권한 기반 접근 제어 수행.
  • 보안 정책 구성 및 필터링(Policy & Filtering)
    • HTTP 요청을 감시 → 인증된 요청만 애플리케이션으로 전달.
    • CSRF 방지, 세션 관리, URL 접근 제어 등등.

스프링 시큐리티 주요 구성 요소

  • SecurityFilterChain

    • HTTP 요청에 대한 게이트 키퍼 역할.
    • 여러 보안 필터가 연결되어 있음. 필터를 통과시키며, 인증 및 인가 과정을 수행한다.
    • 필터들은 개발자가 직접 정의하거나 설정을 통해 등록할 수 있다.
    • 필터 처리는 순차적으로 진행된다.
    • UsernamePasswordAuthenticationFilter: 로그인 요청에서 사용자 이름과 비밀번호를 검증.
    • JwtRequestFilter: 요청 헤더의 JWT를 해석하여 사용자를 인증한다.
    • 요청이 통과되지 못하면, 나머지 필터 실행을 중단 & 401(Unauthorized)or 403(Forbidden) 응답 반환 및 접근을 차단.
  • Authentication

    • 사용자 인증 정보를 담는 객체.
    • 주요 필드
      • principal: 인증된 사용자 ID 또는 주요 정보
      • credentials: 인증에 사용된 비밀번호 및 인증 토큰,
      • authorities: 사용자의 권한 목록.
  • SecurityContext

    • 현재 요청 인증 정보를 저장하는 컨텍스트.
    • 컨텍스트(Context): 현재 요청의 보안 상태 및 인증 정보를 보관하는 환경 또는 저장소.
    • 스프링 시큐리티는 사용자 인증 성공 시, 생성된 Authentication 객체를 SecurityContext에 저장한다. SecurityContextHolder 를 통해 전역적으로 접근할 수 있다.
    • SecurityContextHolder
      • 일반적으로 스레드 로컬(ThreadLocal)에 SecurityContext를 보관한다.
      • 요청이 처리 되는 동안에는 애플리케이션 어느 위치에서나 SecurityContextHolder.getContext().getAuthentication()를 호출해서 현재 로그인한 사용자 인증 정보를 가져올 수 있다.
  • AuthenticationManager, AuthenticationProvider

    • 스프링 시큐리티에서 인증을 처리하는 핵심 인터페이스.
    • 클라이언트 로그인 요청
      → AuthenticationManager 요청 수신 후 1개 이상의 AuthenticationProvider 에게 인증 위임.
      → AuthenticationProvider 는 스스로 처리할 수 있는 인증 방식(사용자 이름/비번, JWT, OAuth2 등)을 확인 한 후 인증 시도.
      → 인증 성공 후 Authentication 객체를 반환.
    • 직접 커스터마이징한 AuthenticationProvider를 구현하여 비표준 인증 로직(사내 인증 서버 연동, 토큰 검증, 다중 인증 등)을 구현 등록할 수 있다.
  • UserDetails, UserDetailsService

    • UserDetails
      • 사용자 계정 정보를 표현하기 위한 추상 인터페이스.
      • username, password, authorities(권한 정보) 등의 필드를 포함한다.
      • 인증 이후 “현재 로그인한 사용자”를 나타낸다.
    • UserDetailsService
      • UserDetails 객체를 데이터소스(데이터베이스, 외부 API 등)에서 조회하는 역할을 담당한다.
      • 사용자 이름(username)을 기준으로 사용자 정보를 로드하고, 결과를 UserDetails 형태로 반환한다.
    • UserDetailsService 를 통해 사용자를 조회한 뒤 입력된 비밀번호를 PasswordEncoder를 이용해 검증하며 인증을 완료한다.
  • GrantedAuthority

    • 사용자의 권한(Authority)을 나타내는 객체.
    • 사용자가 애플리케이션 내에서 수행할 수 있는 행동의 범위를 결정한다.
    • 인증 완료 후, UserDetails 객체의 getAuthorities() 메서드를 통해서 사용자에게 부여된 GrantedAuthority 목록을 확인할 수 있다.
    • 이 권한 정보를 기반으로 특정 URL, 메서드 및 리소스에 대한 접근을 제어한다.
  • Configurer, DSL

    • DSL(Domain-Specific Language 도메인 특화 언어)

      • HttpSecurity 를 통해 요청별 보안 정책을 선언적으로 정의할 수 있는 유연한 API.
    • Configurer

      • DSL 구성을 가능하게 하는 설정 모듈 역할.
    • HttpSecurity

      • 여러 Configurer를 체이닝 방식으로 연결하여 인증, 인가, 세션 관리, CSRF, CORS 등 다양한 보안 설정을 명시적으로 구성한다.
      • 메서드 체이닝을 통해 자연어처럼 읽히는 DSL 스타일로 보안 정책을 작성한다.
      httpSecurity
        .csrf(AbstractHttpConfigurer::disable)
        .authorizeHttpRequests((requests) ->
                requests
                    .requestMatchers("/api/v2/auth/login", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
                    .requestMatchers(HttpMethod.POST, "/api/v2/user").permitAll()
                    .erquestMatchers(HttpMethod.DELETE, "/api/v2/user/{userId}").hasRole("ADMIN")
                    .anyRequest().authenticated()
        );
    • 위 코드에 나온 메서드들처럼 호출되는 메서드는 각자의 Configurer를 내부적으로 호출하고, 시큐리티 필터 체인(SecurityFilterChain)에 설정을 추가하게 된다.

  • JwtUtil ⇒ 이 클래스는 제공되는 게 아니라 프로젝트에서 만들어서 사용하는 것임.

    • JWT 생성, 검증, 클레임(Claims) 추출 등의 기능을 담당하는 유틸리티 클래스.
    • 제공되는 기능
      • 토큰 생성: 사용자 정보(username, roles 등)를 포함한 JWT 발급.
      • 토큰 검증: 서명 유효성 및 만료 시간 검증.
      • 클레임 추출: JWT 에서 사용자 이름, 권한 등 필요한 정보 추출.
    • 토큰 관련 로직을 한 곳에 모아 관리한다.
    • 인증 필터(JwtAuthenticationFilter 등등)에 대한 결합도를 낮추고, 테스트 및 유지 보수 하기 쉽게 한다.
    • 토큰 생성, 검증 로직을 캡슐화해 인증 흐름과 분리하는 역할을 수행하는 것이 중요하다.

JWT 인증 과정

  1. JWT 검증.
    • JwtRequestFilter: 요청 헤더에서 JWT를 추출하고 JwtUtil을 통해 검증한다.
    • JwtUtil: 토큰의 유효성(서명, 만료 시간 등)을 확인한 후, 사용자 이름과 권한 정보를 추출한다.
    • 검증 및 정보 추출 후 이 기반으로 Authentication 객체를 생성한다.
    • Authentication 객체에는 사용자 권한 정보(GrantedAuthority)가 포함된다.
  2. 사용자 정보 로딩.
    • AuthenticationManager: AuthenticationProvider를 통해 사용자 인증을 처리한다.
    • 인증 과정에서 UserDetailsService 가 데이터베이스에서 사용자 정보를 로딩한다.
    • 로딩된 정보를 바탕으로 UserDetails 객체가 생성되며, 이 객체에는 사용자 권한 정보(GrantedAuthority)도 포함된다.
  3. SecurityContext 에 인증 정보 저장.
    • 인증 성공 후, 생성된 Authentication 객체는 SecurityContextHolder를 통해 현재 스레드의 SecurityContext 에 저장된다.
    • 이 객체는 요청의 생명 주기 동안 유지 된다.
    • 이후 권한 검증에 활용된다.
  4. 권한 검증.
    • 요청된 URL에 대해 사용자 권한(GrantedAuthority)이 필요한 수준에 도달하는지 검사한다.
    • 권한 확인 과정에서 SecurityContext 에 저장된 GrantedAuthority를 참조한다.
    • 적합한 권한이 없는 경우, 403 Forbidden 응답을 반환한다.
    • 권한 검사가 성공하면 요청이 컨트롤러로 전달되고 정상 처리 된다.

Project 설정

build.gradle 스프링 시큐리티 의존성 추가

dependencies {
    ...

  // Spring Security, JWT
  implementation 'org.springframework.boot:spring-boot-starter-security'

  // JWT
  implementation 'io.jsonwebtoken:jjwt-api:0.11.5'

  // Spring Security Test
  testImplementation 'org.springframework.security:spring-security-test'

  // JWT Test
  runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
  runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

    ...
}

application.properties 에 비밀 키secret key 추가

jwt.secret=비밀키

⚠️ 비밀키 하드 코딩 금지 ⚠️

인증 기능 구현

JWT 생성 및 검증 구현

  • GrantedAuthority 상속하여 커스텀 GrantedAuthority 직접 정의하기.
  • UserDetails, UserDetailsService 각각 상속한 커스텀 클래스 직접 정의하기.
  • JWT 인증 처리 구현하기.
  • SecurityFilterChain 설정하기.
  • SecurityContext 보안 컨텍스트 설정하기.
  • 엔드포인트에 대한 보안 설정하기.

GrantedAuthority 상속하여 커스텀 GrantedAuthority 직접 정의하기

  • 커스텀 권한 로직 구현.
  • 도메인 객체와의 통합.
  • 확장성.

UserDetails, UserDetailsService 각각 상속한 커스텀 클래스 직접 정의하기

  • UserDetails: 사용자 정보를 캡슐화 하는 인터페이스. 사용자명, 비밀번호, 권한 등의 정보를 제공한다.
  • UserDetailsService: UserDetails 를 로드하는 서비스 인터페이스. 사용자 인증 시 필요한 사용자 정보를 제공한다.

JWT 인증 처리 구현

  • JwtUtil: JWT 생성, 검증 유틸리티 클래스.
  • JwtRequestFilter: JWT 활용하여 요청을 필터링 하는 필터.
  • JwtAuthenticationEntryPoint: 인증 실패 할 경우에 대한 동작 정의.
  • AuthenticationController: 로그인 요청 처리 컨트롤러.

JwtUtil

  • 비밀키 관리

      // application.properties 에 정의된 jwt.secret 값을 주입힌다.
      @Value("${jwt.secret}")
      private String jwt;

JwtRequestFilter

  • OncePerRequestFilter 상속
    • OncePerRequestFilter : 필터 로직이 요청 1번에 1번만 실행되도록 보장하는 안전 장치. 스프링 시큐리티 필터들의 공통 기반 클래스. 요청 하나당 각 필터가 한번씩만 실행되는 것을 보장 받는다.
  • 토큰 추출 및 검증
    • Authorization 헤더에서 JWT 추출.
    • ‘Bearer’ 로 시작하는 경우에 대해 확인, 실제 토큰 값 얻기.
    • 토큰에서 사용자명 추출하고, 토큰 유효성 검증.
  • SecurityContext 설정
    • 유효 토큰인 경우, UsernamePasswordAuthenticationToken 생성 후 SecurityContextHolder에 설정함.
    • 이를 통해 인증된 사용자로 처리 된다.
    • 애플리케이션 내에서 인증 정보를 사용할 수 있게 된다.
  • 예외 처리 및 로그 메시지.

JwtAuthenticationEntryPoint

  • 인증이 필요한 엔드포인트에 비인증 상태로 접근 할 경우 발생하는 예외를 처리한다.

  • AuthenticationEntryPoint 인터페이스를 구현하여 인증 오류에 대한 응답을 처리한다.

    • 스프링 시큐리티에서 ‘미인증 사용자가 보호된 리소스에 접근할 경우, 인증 절차를 어떻게 시작할 것인지’를 정의하느 인터페이스.
  • 인증이 필요한 요청이 인증 없이 들어 올 때, 가장 먼저 호출되는 진입점(entry point) 이다.

    • commence 메서드 구현

      • 개시하다.
      • 인증 절차를 개시하는 메서드.
      • 인증 예외 발생 시 호출된다.
      • HTTP 상태 코드 401(Unathorized) 와 함께 JSON 형식의 에러 메시지를 응답으로 보낸다.
      • response.setStatus(HttpServletResponse.SC_UNATHORIZED) 를 통해 상태 코드를 설정한다.
    • 에러 메시지 설정.

      • Map 을 사용해 응답 본문에 포함할 데이터를 구성한다.
      • ObjectMapper 를 사용해 맵을 JSON 문자열로 변환한다.
    • Content-Type 설정.

      • reponse.setContentType(”application/json”) 을 통해 응답 컨텐츠 타입을 JSON 으로 지정한다.
      • 클라이언트가 JSON 형식 에러 메시지를 적절하게 처리할 수 있도록 한다.

      AuthenticationController

    • 엔드포인트 설정

    • 의존성 주입

    • 인증 처리.

    • JWT 생성 및 변환

    • 예외 처리.

    • 응답 구조.

SecurityConfig

  • Bean 등록 처리.

    • PasswordEncoder: 사용자 암호 인코딩한다.

        // PasswordEncoder 빈 설정하기
        @Bean
        public PasswordEncoder passwordEncoder() {
          return new BCryptPasswordEncoder();
        }
      • 스프링 시큐리티는 비밀번호를 평문으로 저장하거나 비교하지 않는다.
      • 항상 PasswordEncoder 를 빈으로 등록해 사용해야 한다. 빈이 등록되지 않으면 인증 과정에서 “No PasswordEncoder mapped for the id” 예외가 발생한다.
      • BCryptPasswordEncoder: 비밀번호를 단방향 해시함수로 암호화한다. 같은 비밀번호도 매번 다른 해시 값을 생성한다. 무차별 대입 공격 또는 무작위 해시 공격에 강력한 보안성을 제공한다.
    • AuthenticationManager: 자격 증명을 검증한다.

        // AuthenticationManager 빈 설정
        @Bean
        public AuthenticationManager authenticationManagerBean(
            AuthenticationConfiguration configuration
        ) throws Exception {
          return configuration.getAuthenticationManager();
        }
      • AuthenticationManager: 스프링 시큐리티 인증 과정을 총괄하는 핵심 인터페이스. 사용자 자격 증명(username 과 password)을 확인해 이를 기반으로 사용자가 애플리케이션에 접근할 수 있는지 여부를 결정한다.
      • AuthenticationConfiguration 을 통해서 기본 제공되는 DaoAuthenticationProvider 를 사용한다.
        • UserDetailsService 를 통해 사용자 정보를 로딩하고 PasswordEncoder 를 사용해서 비밀번호를 검증한다.
    • SecurityFilterChain: 보안 정책을 설정한다.

        // SecurityFilterChain 빈 설정
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
            httpSecurity
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(
                    (requests) -> requests
                        .requestMatchers("/api/v2/auth/login", "/swagger-ui/**", "v3/api-docs/**")
                        .permitAll()
                        // 사용자 생성, 조회는 인증 없이 가능하도록 조치.
                        .requestMatchers(HttpMethod.POST, "/api/v2/user")
                        .permitAll()
                        .requestMatchers(HttpMethod.GET, "/api/v2/user/{userId}")
                        .permitAll()
                        // 사용자 삭제는 어드민 권한이 필요하도록 조치.
                        .requestMatchers(HttpMethod.DELETE, "/api/v2/user/{userId}")
                        .hasRole("ADMIN")
                        .anyRequest()
                        .authenticated())
                .exceptionHandling(
                    exceptionHandling -> exceptionHandling.authenticationEntryPoint(jwtAuthenticationEntryPoint))
                .sessionManagement(
                    sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
            httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
            return httpSecurity.build();
        }
      • SecurityFilterChain 빈: 여러 보안 필터로 구성된 시큐리티 필터 체인 을 정의한다. HTTP 요청에 대해 이 필터 체인을 통과하고, 인증 및 인가 절차가 순차적으로 수행된다.

      • CSRF 보호 비활성화

        • csrf(AbstractHttpConfigurer::disable) 를 통해서 CSRF 보호를 비활성화 한다.
        • REST API 는 일반적을 세션 기반이 아니며, CSRF 토큰을 사용하는 폼 요청이 없기 때문에 CSRF 공격의 노출 가능성이 낮다. 그래서 CSRF 보호 기능을 비활성화한다.
      • 요청 인증 설정

        • requestMatchers 를 통해 매칭되는 엔드포인트에 대해 필요한 인증 조치를 처리한다.
        • anyRequest().authenticated() 를 통해서 나머지 엔드포인트에 대한 요청은 인증된 사용자만 접근하게 한다.
      • 예외 처리 설정

        • exceptionHandling 을 통해서 인증 실패 시 JwtAuthenticationEntryPoint 를 사용해서 처리하도록 설정한다.
        • 인증되지 않은 사용자가 보호된 리소스에 접근을 시도하면 JwtAuthenticationEntryPoint 가 호출되어 401 Unauthorized 또는 403 Forbidden 응답을 반환한다.
      • 세션 관리 설정

        • JWT 를 사용하는 경우 서버 측 세션이 필요 없다.
        • sessionManagement() 에서 SessionCreationPolicy.STATELESS 를 설정해, 서버가 세션을 생성하거나 유지하지 않도록 한다.
      • JWT 필터 추가

          httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
        • JWT 기반 인증을 기존 인증 필터 보다 앞에서 수행하도록 설정한다.
        • 요청 헤더의 토큰 검증이 먼저 이뤄지고, 유효한 토큰의 경우 사용자 정보가 SecurityContext에 설정되어 컨트롤러 접근 시 인증된 사용자로 인식된다.
  • 엔티티와 DTO

    엔티티

    • Role 필드 추가

          @ElementCollection(fetch = FetchType.EAGER)
          @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
          @Enumerated(EnumType.STRING)
          @Column(name = "role")
          private Set<Role> roles;
      • 역할 정보를 데이터베이스 user_roles 테이블에 문자열로 저장하고, 즉시 로드할 수 있도록 설정한다.
    • 비밀번호 자동 암호화하여 저장.

      • lombok.Builder 를 제거하고, 커스텀 빌더 클래스 추가.
      • 비밀번호 암호화 처리하고 제어할 수 있도록 커스텀 빌드 로직 구축.

    컨트롤러

    • @AuthenticationPrincipal
      • JWT 를 통해 인증된 사용자 정보를 가져온다.
      • UserDetails 객체를 통해서, 인증된 사용자 정보를 주입받는다.
    • @PreAuthorize("hasRole('ADMIN')") // Only admins can delete users
      • 관리자 권한(여기서는 ‘ADMIN’) 이 있는 사용자만 접근 가능하도록 설정한다.
      • 메서드별로 세부적인 보안 설정을 추가할 수 있다.
    • 마지막 JWT 인증 과정은 전체 도감이 필요해서 찾아봐야겠다.
    반응형