java

Spring Security + JWT 통합 인증 구현 방법

개발에대해 2025. 10. 1. 09:00
반응형

 

Spring Security + JWT 통합 인증 구현 방법

안녕하세요! 오늘은 많은 개발자분들이 궁금해하는 Spring Security + JWT(JSON Web Token) 통합 인증 구현 방법을 단계별로 정리해보려고 합니다. OAuth2처럼 외부 서비스를 이용하지 않고, 자체 로그인 시스템을 만들고 싶은 경우 JWT는 아주 유용합니다. 그럼, 시작해보겠습니다! 

 

1. 왜 JWT를 사용할까?

JWT는 사용자의 인증 상태를 토큰에 담아 클라이언트와 서버가 주고받는 방식이에요.

서버가 세션을 직접 관리할 필요가 없으니 무상태(stateless) 아키텍처에 딱 맞죠.

  • 세션 저장소가 필요 없음 → 확장성(Scalability) 확보
  • 모바일, 웹 클라이언트 등 다양한 환경에서 쉽게 사용 가능
  • Access Token + Refresh Token 전략으로 보안 강화 가능

 

2. 프로젝트 의존성 추가

Spring Boot + Security + JWT 구현을 위해 아래 의존성을 추가해줍니다.


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-api</artifactId>
  <version>0.11.5</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-impl</artifactId>
  <version>0.11.5</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-jackson</artifactId>
  <version>0.11.5</version>
  <scope>runtime</scope>
</dependency>
      

 

3. JWT 유틸리티 클래스 만들기

JWT 토큰을 생성하고 검증하는 유틸리티 클래스를 작성해봅시다.


import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
public class JwtUtil {
    private final String SECRET = "mySecretKey123";
    private final long EXPIRATION_TIME = 1000 * 60 * 60; // 1시간

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256, SECRET)
                .compact();
    }

    public String getUsername(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
      

 

4. JWT 필터 구현하기

요청(Request)이 들어올 때 JWT를 검사하는 필터를 작성해야 합니다.

이 필터는 Spring Security의 OncePerRequestFilter를 상속받아 구현할 수 있어요.


import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtUtil jwtUtil;

    public JwtAuthenticationFilter(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
                                    throws ServletException, IOException {
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            if (jwtUtil.validateToken(token)) {
                String username = jwtUtil.getUsername(token);
                UsernamePasswordAuthenticationToken authToken =
                        new UsernamePasswordAuthenticationToken(username, null, null);
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
        filterChain.doFilter(request, response);
    }
}
      

 

5. SecurityConfig 설정

이제 우리가 만든 JWT 필터를 Spring Security 설정에 추가해야 합니다.


import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final JwtUtil jwtUtil;

    public SecurityConfig(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(new JwtAuthenticationFilter(jwtUtil),
                             UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}
      

 

6. 로그인 & 토큰 발급 API

이제 사용자가 로그인하면 JWT를 발급해주는 컨트롤러를 만들어봅시다.


import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/auth")
public class AuthController {
    private final JwtUtil jwtUtil;

    public AuthController(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        // 실제 구현에서는 DB 검증 로직 필요
        if ("user".equals(username) && "1234".equals(password)) {
            return jwtUtil.generateToken(username);
        }
        throw new RuntimeException("Invalid credentials");
    }
}
      

 

이제 /auth/login에 요청하면 JWT가 반환되고, 이 토큰을 Authorization 헤더에 담아 다른 API를 호출할 수 있습니다.

 

7. 마무리

오늘은 Spring Security와 JWT를 결합해 인증 시스템을 구현해보았습니다.

핵심은 JWT 생성 → 요청 시 검증 → SecurityContext에 인증 정보 저장이라는 흐름이에요.

여기서 더 나아가려면 Refresh Token, 토큰 블랙리스트 처리, Role 기반 권한 관리 같은 기능을 붙여볼 수 있습니다. 

 

 

반응형