Spring Security + JWT

2025. 2. 21. 16:58·Spring

1. Spring Security vs. JWT Security 차이점

Spring Security와 JWT Security는 서로 대체 관계가 아니라,

JWT가 Spring Security의 인증(Authentication) 방식 중 하나로 사용될 수 있다.

목적 애플리케이션 전반적인 인증(Authentication)과 권한(Authorization) 관리 JWT(JSON Web Token) 기반의 인증 방식
기본 인증 방식 세션 기반 인증 (Session, Cookie, HTTP Basic, OAuth 등) 토큰 기반 인증 (Bearer Token 사용)
서버 상태 세션 저장소 필요 (Stateful) 세션 없이 동작 (Stateless)
사용 사례 내부 서비스, 관리자 페이지, 전통적인 웹 애플리케이션 REST API, 모바일 앱, 마이크로서비스
보안 관리 세션/쿠키 기반 관리 JWT 서명(Signature) 검증

2. 토큰(Token)이란?

  • 사용자의 인증 정보를 포함하는 문자열로, 주로 JSON Web Token(JWT) 형태로 사용됨.
  • 클라이언트가 로그인 후 토큰을 받아서 요청마다 포함해서 전송.
  • 서버는 토큰을 검증하는 것만으로 인증이 가능하며, 세션을 저장할 필요가 없음 (stateless)

3. JWT Security 개념 및 동작 방식


🔹 JWT는 3가지 부분으로 구성됨 -> Header.Payload.Signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.  // Header (알고리즘, 토큰 타입)
eyJ1c2VySWQiOiIxMjM0NTYiLCJyb2xlIjoiVVNFUiJ9.  // Payload (사용자 정보)
sfsdfIjd83jFksjf83JSdfLdsf9f8SDfdf  // Signature (서명)
  • Header → 어떤 알고리즘을 사용할지 (alg: HS256 등)
  • Payload → 사용자 ID, 권한 정보 등 (Base64로 인코딩됨)
  • Signature → 변조 방지를 위한 서명 (서버의 비밀 키로 서명됨)

 

🔹 JWT 인증 과정

  1. 로그인 요청
    • 사용자가 ID/PW 입력 후 로그인 요청 (POST /login)
  2. JWT 토큰 발급
    • 서버가 ID/PW 검증 후 JWT 토큰을 생성하여 클라이언트에 반환
  3. 클라이언트 저장
    • 클라이언트(웹, 모바일)는 토큰을 로컬 스토리지, 세션 스토리지 또는 쿠키에 저장
  4. 요청 시 토큰 포함
    • 이후 API 요청을 보낼 때, Authorization 헤더에 토큰을 포함
    • Authorization: Bearer <JWT_TOKEN>
  5. 서버 검증 및 처리
    • 서버는 토큰의 서명을 검증하고, 유저 정보를 확인한 후 요청을 처리

 

1. JWT 생성 (JwtUtil = JwtProvider)
2. JWT 필터 (JwtAuthenticationFilter)
3. Spring Security 설정 (SecurityConfig)

 

3-1. JwtProvider란?

JwtProvider(혹은 JwtUtil, JwtTokenProvider)는 JWT 토큰을 생성하고, 검증하는 역할을 하는 클래스이다.
(Spring Security에서 JWT 기반 인증을 사용할 때 필수적인 유틸리티 클래스)

 

🔹 JwtProvider의 주요 역할

  1. JWT 토큰 생성
    • 사용자가 로그인하면, 사용자 정보를 포함한 JWT 액세스 토큰을 생성
    • 보통 만료 시간을 설정하고, 비밀 키(secret key)로 서명함
  2. JWT 토큰 검증
    • 클라이언트가 보낸 토큰이 유효한지 검증
    • 변조 여부를 확인하고, 서명 검증을 수행
    • 만료된 토큰인지 확인
  3. JWT에서 사용자 정보 추출
    • 토큰에서 사용자 ID, 권한(role) 같은 정보를 가져와서 인증 객체를 생성.

✔JwtProvider 예제 코드

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
public class JwtProvider {

    private final String SECRET_KEY = "mysecretkey"; // 🔹 비밀 키 (보안 강화를 위해 .env로 관리 권장)
    private final long EXPIRATION_TIME = 1000 * 60 * 60; // 🔹 1시간 (토큰 유효 시간)

    // ✅ 1. JWT 토큰 생성
    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)  // 사용자 이름 (주로 userId 사용)
                .setIssuedAt(new Date()) // 발급 시간
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // 만료 시간
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 서명
                .compact();
    }

    // ✅ 2. JWT 토큰 검증
    public boolean validateToken(String token) {
        try {
            Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token); // 서명 검증
            return true;
        } catch (Exception e) {
            return false; // 유효하지 않은 토큰
        }
    }

    // ✅ 3. 토큰에서 사용자 이름(username) 추출
    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject(); // setSubject()에 저장한 값 가져오기
    }
}

 

  • 로그인 과정
  1. 클라이언트가 ID/PW로 로그인 요청
  2. 서버가 사용자 인증 후 JWT 액세스 토큰을 생성 (JwtProvider.generateToken())하여 클라이언트에게 전달
  3. 클라이언트는 이후 API 요청마다 Authorization: Bearer <JWT> 헤더에 토큰을 포함해서 보냄
  • JWT 검증 과정
  1. 클라이언트가 요청을 보낼 때 JWT를 헤더에 포함
  2. JwtAuthenticationFilter가 토큰을 추출하여 JwtProvider.validateToken()으로 검증
  3. 유효한 토큰이면 JwtProvider.getUsernameFromToken()으로 사용자 정보를 가져옴
  4. UserDetailsService를 통해 사용자 정보를 로드하고 인증을 완료
  5.  

 

3-2. JwtProvider와 JwtAuthenticationFilter

 

Spring Security에서 JWT를 사용할 때, JwtAuthenticationFilter가 요청을 가로채서 인증을 수행하는데,
그 내부에서 JwtProvider를 이용해 토큰을 검증하고 사용자 정보를 가져오는 것이 핵심 흐름이다.

 

✔ JwtAuthenticationFilter 예제

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtProvider jwtProvider;
    private final UserDetailsService userDetailsService;

    public JwtAuthenticationFilter(JwtProvider jwtProvider, UserDetailsService userDetailsService) {
        this.jwtProvider = jwtProvider;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        
        String token = getTokenFromRequest(request);
        
        if (token != null && jwtProvider.validateToken(token)) { // ✅ 토큰 검증
            String username = jwtProvider.getUsernameFromToken(token); // ✅ 토큰에서 사용자 정보 가져오기
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken auth =
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(auth); // ✅ 인증 완료
        }
        
        chain.doFilter(request, response); // 다음 필터로 이동
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7); // "Bearer " 제거 후 토큰만 추출
        }
        return null;
    }
}

 

 


 

4. 전체 Spring Security + JWT 인증 흐름

1️⃣ 로그인 요청 (UsernamePasswordAuthenticationFilter)

  1. 사용자가 ID/PW로 로그인 요청을 보냄. (POST /login)
  2. UsernamePasswordAuthenticationFilter가 요청을 가로채고, UserDetailsService를 이용해 사용자 인증 수행.
  3. 인증이 성공하면 JwtProvider.generateToken()을 호출해 JWT 액세스 토큰을 생성.
  4. 생성된 토큰을 클라이언트에게 응답으로 반환. (헤더 또는 JSON 응답)

2️⃣ JWT 기반 인증 (JwtAuthenticationFilter)

  1. 클라이언트는 이후 요청마다 Authorization 헤더에 JWT 포함하여 보냄.
    Authorization: Bearer <JWT>
    
  2. JwtAuthenticationFilter가 요청을 가로채고, 헤더에서 JWT를 추출.
  3. JwtProvider.validateToken()을 호출하여 토큰의 유효성을 검증.
  4. JwtProvider.getUsernameFromToken()을 호출하여 사용자 정보(ID)를 추출.
  5. UserDetailsService.loadUserByUsername()을 호출하여 사용자 정보 조회.
  6. 인증이 완료되면 SecurityContextHolder에 인증 정보를 저장하고, 해당 요청을 계속 진행하도록 허용.

요약

1️⃣ 로그인 요청 → UsernamePasswordAuthenticationFilter → 인증 성공 → JwtProvider로 JWT 생성
2️⃣ 클라이언트가 JWT 포함하여 요청 → JwtAuthenticationFilter가 검증 → 인증된 요청 처리

'Spring' 카테고리의 다른 글

Ports and Adapter 패턴 (Hexagonal)  (0) 2025.02.17
@RequestPart 알아보기  (0) 2025.02.12
열어봐요 @RequestBody로 데이터 받는 과정  (0) 2025.02.10
@RequestBody 와 <form> 태그  (1) 2025.02.05
Spring Pageable 안 쓰면 바보  (1) 2025.01.31
'Spring' 카테고리의 다른 글
  • Ports and Adapter 패턴 (Hexagonal)
  • @RequestPart 알아보기
  • 열어봐요 @RequestBody로 데이터 받는 과정
  • @RequestBody 와 <form> 태그
힐안
힐안
나 지금 학비 내면서 개발 독학하잖아.
  • 힐안
    후라이
    힐안
  • 전체
    오늘
    어제
    • 분류 전체보기 (59)
      • HTTP (9)
      • Spring (28)
        • 웹 게시판 (4)
      • Neural Network (1)
        • CNN (1)
        • RNN (0)
      • Deep Learning (6)
      • Audio (3)
      • 알고리즘(JAVA) (2)
      • 백준 (10)
        • Bronze (0)
        • Silver (2)
        • Gold (8)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    dcgan
    백준
    플로이드와샬
    API
    Spring
    프레임워크
    최단경로알고리즘
    springboot
    GAN
    딥러닝
    HTTP
    MNIST
    스프링부트
    자바
    오블완
    티스토리챌린지
    MVC
    웹
    JSON
    벨만포드
    web
    백엔드
    비지도학습
    Genrative_model
    다익스트라
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
힐안
Spring Security + JWT
상단으로

티스토리툴바