Spring Boot + JWT 로그인 인증 구현 (간단 예제)

dev.hyjang·2025년 8월 7일
post-thumbnail

🔐 Spring Boot + JWT 로그인 인증 구현하기 (초간단 예제)

이번 포스트에서는 Spring Boot를 기반으로 JWT(Json Web Token)를 이용한 로그인 인증 기능을 구현하는 과정을 단계별로 정리합니다.


✅ 사용 기술 스택

  • Java 17
  • Spring Boot
  • Spring Security
  • H2 Database
  • Lombok
  • jjwt (JWT 라이브러리)

📁 프로젝트 구조 개요

src/
└─ com.ccp.simple
├─ controller
│ └─ AuthController.java
├─ security
│ └─ JwtTokenProvider.java
├─ config
│ └─ SecurityConfig.java
├─ service
│ └─ UserService.java
└─ dto
└─ LoginRequestDto.java


🔐 JWT란? 왜 사용하는가?

JWT (JSON Web Token)는 사용자의 인증 정보를 안전하게 전달하기 위한 토큰 기반 인증 방식입니다.

✅ 특징

  • Stateless: 서버가 세션/쿠키를 저장하지 않음 → 서버 확장에 유리
  • Self-contained: 토큰 자체에 유저 정보(예: ID, Role 등)를 담고 있어 별도 DB 조회 없이 인증 가능
  • Bearer Token 방식 → HTTP 요청의 Authorization 헤더에 담아 전달

🔁 전체 인증 흐름 요약

  1. 로그인 요청 : 사용자가 /api/login에 ID/PW를 보내면 서버는
  • 사용자 인증 (UserService에서 유효성 확인)
  • JWT 토큰 생성 (JwtTokenProvider.createToken)
  • Authorization: Bearer {token} 헤더에 담아 응답
  1. 클라이언트는 받은 토큰을 저장 : 로컬 스토리지 / 세션 스토리지 / 쿠키 등에 저장

  2. 인증이 필요한 요청마다 토큰을 같이 전송
    GET /api/me HTTP/1.1
    Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

  3. 서버는 요청 시 토큰을 검증
    JwtTokenProvider.validateToken()으로 유효성 체크
    토큰이 유효하면 SecurityContext에 사용자 정보 설정
    이후 컨트롤러 등에서 Authentication 객체로 사용자 정보 사용 가능

✅ 정리

  1. 로그인 : ID/PW로 JWT 토큰 발급
  2. 클라이언트 저장 : 받은 토큰을 저장 후 인증 요청에 포함
  3. 인증 요청 : Authorization 헤더에 Bearer {token} 포함
  4. 토큰 검증 : 서버에서 토큰 검증 후 사용자 인증 처리
  5. 사용자 정보 접근 : Authentication 객체로 현재 사용자 확인 가능

🔧 1. JWT 토큰 생성 및 검증 클래스

// JwtTokenProvider.java
@Component
public class JwtTokenProvider {

    private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    private final long validityInMilliseconds = 3600000; // 1시간

    public String createToken(String userId) {
        Claims claims = Jwts.claims().setSubject(userId);
        Date now = new Date();
        Date expiry = new Date(now.getTime() + validityInMilliseconds);
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(expiry)
                .signWith(key)
                .compact();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }

    public String getUserId(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build()
                .parseClaimsJws(token).getBody().getSubject();
    }
}

🛡️ 2. Spring Security 설정


// SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .headers(headers -> headers.frameOptions().disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/h2-console/**", "/api/login", "/login.html").permitAll()
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

/h2-console/**, /api/login 경로는 인증 없이 접근 가능하도록 허용합니다.

🧪 3. 로그인 및 인증 컨트롤러

// AuthController.java
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class AuthController {

    private final UserService userService;
    private final JwtTokenProvider jwtTokenProvider;

    @PostMapping("/login")
    public String login(@RequestBody LoginRequestDto request, HttpServletResponse response) {
        boolean valid = userService.validateUser(request.getUserId(), request.getUserPassword());
        if (valid) {
            String token = jwtTokenProvider.createToken(request.getUserId());
            response.setHeader("Authorization", "Bearer " + token);
            return "Login successful";
        } else {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return "Invalid credentials";
        }
    }

    @GetMapping("/me")
    public String currentUser(Authentication authentication) {
        if (authentication == null) {
            return "No Authentication found";
        }
        return "Current User ID : " + authentication.getPrincipal();
    }
}

📬 4. Postman으로 테스트

✔️ 1) 로그인 요청 (POST)
URL: http://localhost:8080/api/login

✔️ 2) 인증된 사용자 정보 요청 (GET)
URL: http://localhost:8080/api/me

profile
낭만감자

1개의 댓글

comment-user-thumbnail
2025년 8월 7일

퍼가요~♡

답글 달기