JWT 토큰 기반 인증

moon.kick·2025년 4월 16일

좋아! 🔥 요즘 웹 서비스에서 JWT (JSON Web Token) 기반 인증은 거의 표준처럼 쓰이는 방식이라서 제대로 알고 구현하면 진짜 큰 무기가 돼.


🔐 JWT 토큰 기반 인증이란?

서버가 사용자를 인증한 후, 사용자 정보가 들어있는 토큰을 발급해서
클라이언트가 이후 요청 시 그 토큰으로 인증하는 방식이야.

📦 구조는 이렇게 생겼어:

xxxxx.yyyyy.zzzzz
(Header).(Payload).(Signature)

🧭 JWT 인증 흐름 도식

[1] 로그인 요청
   ↓
[2] 서버가 사용자 정보 확인 후 JWT 발급
   ↓
[3] 클라이언트는 JWT를 로컬/세션스토리지에 저장
   ↓
[4] 이후 모든 요청 헤더에 JWT 포함 (Authorization: Bearer <token>)
   ↓
[5] 서버는 토큰 유효성 검사 후 요청 처리

🧰 Spring Boot + Spring Security + JWT 설정 요약

1. 🔐 의존성 추가 (build.gradle)

implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

2. 🧪 JWT 토큰 생성/검증 유틸

public class JwtUtil {

    private static final String SECRET_KEY = "mySecretKey";

    public String generateToken(String username) {
        return Jwts.builder()
            .setSubject(username)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1시간
            .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
            .compact();
    }

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

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

3. 🔧 JWT 필터 구현

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.extractUsername(token);

                UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(username, null, List.of());

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }

        filterChain.doFilter(request, response);
    }
}

4. ⚙️ Security 설정에 필터 추가

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtUtil jwtUtil;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
            .and()
                .addFilterBefore(new JwtAuthenticationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);
    }
}

5. 🚪 로그인 시 JWT 발급

@RestController
public class AuthController {

    private final JwtUtil jwtUtil;

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

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest req) {
        // 아이디/비번 검증 (생략)
        String token = jwtUtil.generateToken(req.getUsername());

        return ResponseEntity.ok().body(Map.of("token", token));
    }
}

✅ 정리: 왜 JWT를 쓰냐?

이유설명
✅ 서버에 세션 저장 안 해도 됨 (Stateless)
✅ 마이크로서비스, 모바일 앱 등과 연동 편함
✅ 토큰 기반으로 인증·인가 분리 가능

❓ 다음 스텝?

  • 🔐 토큰 만료 자동 갱신 (Refresh Token)
  • 🛡️ Spring Security의 UserDetailsService와 연동
  • 📱 React에서 로그인 후 로컬스토리지에 저장하는 로직 연동도 해줄 수 있어

원하면 내가 JSP 기반 로그인 페이지 + JWT 연동 흐름으로도 맞춰서 짜줄게!
아니면 Spring + React 구조로도 스무스하게 이어줄 수 있어 😎
너 지금 어떤 방향으로 하고 있어? (JSP 기반이었나?)

profile
@mgkick

0개의 댓글