728x90
JWT 인증이란?
정의
**JWT(Json Web Token)**은 인증 정보를 담은 서명된 토큰을 클라이언트에 전달하여,
세션 없이 Stateless하게 사용자 인증을 유지하는 방식입니다.
🧱 JWT 구조
JWT는 3개의 부분으로 구성됩니다:
HEADER.PAYLOAD.SIGNATURE
시:
eyJhbGciOiJIUzI1NiJ9.
eyJ1c2VybmFtZSI6ImRhbmJpIiwicm9sZXMiOlsiVVNFUiJdfQ.
sdf934jfsd98fsdljf934jsdf
| 구성 요소 | 설명 |
| Header | 토큰 타입 + 알고리즘 정보 |
| Payload | 사용자 정보(Claims) |
| Signature | 비밀 키를 사용한 서명 |
JWT 인증 전체 흐름
- 사용자가 로그인 시도 (ID/PW)
- 서버가 인증 후 JWT 생성
- 클라이언트에 JWT 전달 (HTTP 응답)
- 이후 요청마다 Authorization: Bearer {token} 헤더에 포함
- 서버는 토큰을 검증하여 사용자 인증
1. 의존성 추가 (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 토큰 생성 및 검증 유틸
@Component
public class JwtTokenProvider {
private final String secretKey = "secret_key_example"; // 반드시 길게!
// 토큰 유효 시간 (1시간)
private final long validityInMilliseconds = 3600000;
// 토큰 생성
public String createToken(String username, List<String> roles) {
Claims claims = Jwts.claims().setSubject(username);
claims.put("roles", roles);
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(Keys.hmacShaKeyFor(secretKey.getBytes()), SignatureAlgorithm.HS256)
.compact();
}
// 토큰에서 사용자 이름 추출
public String getUsername(String token) {
return Jwts.parserBuilder()
.setSigningKey(secretKey.getBytes())
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
// 토큰 유효성 검사
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(secretKey.getBytes())
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
3. JWT 필터 생성
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = resolveToken(request);
if (token != null && jwtTokenProvider.validateToken(token)) {
String username = jwtTokenProvider.getUsername(token);
Authentication auth = new UsernamePasswordAuthenticationToken(username, "", List.of());
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
String bearer = request.getHeader("Authorization");
if (bearer != null && bearer.startsWith("Bearer ")) {
return bearer.substring(7);
}
return null;
}
}
4. SecurityConfig에 필터 등록
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtTokenProvider jwtTokenProvider;
public SecurityConfig(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
5. 로그인 API에서 JWT 발급
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auth")
public class AuthController {
private final JwtTokenProvider jwtTokenProvider;
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody MemberDto dto) {
Member member = memberRepository.findByUsername(dto.getUsername())
.orElseThrow(() -> new RuntimeException("사용자 없음"));
if (!passwordEncoder.matches(dto.getPassword(), member.getPassword())) {
throw new RuntimeException("비밀번호 불일치");
}
String token = jwtTokenProvider.createToken(member.getUsername(), List.of("ROLE_USER"));
return ResponseEntity.ok(Map.of("token", token));
}
}
6. 클라이언트 요청 시 토큰 포함
GET /api/user/info HTTP/1.1
Host: localhost:8080
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
마무리 요약
| 항목 | 설명 |
| JWT | 사용자 정보를 담은 서명된 토큰 |
| 구성 | Header.Payload.Signature |
| 사용 방식 | 로그인 시 토큰 발급 → 클라이언트가 요청마다 토큰 포함 |
| 스프링 필수 구성 | JwtTokenProvider, JwtAuthenticationFilter, SecurityConfig |
LIST
'SpringBoot > 보안과 인증' 카테고리의 다른 글
| OAuth2 로그인(Google, Kakao 등) (0) | 2025.04.15 |
|---|---|
| 로그인 & 회원가입 구현 (0) | 2025.04.15 |
| Spring Security 기본 설정 (0) | 2025.04.15 |