45. [ JAVA ] Spring Boot에 JWT적용하여 구현: 기초 세팅
Spring Boot에 JWT(Json Web Token)에 대하여 알아보았았습니다.
intelliJ
위와 같이 Postman 에서 로그인을 진행하면 token이 발행될 수 있도록
intelliJ에서 초기 작업을 진행하도록 하겠습니다.
초기 세팅을 잘해두면 차후 클라이언트의 요청에 의해 몇 가지만 수정하여 재사용이 가능합니다.
intelliJ에 아래 문구를
application.yml 파일 설정
1. Config
설정 파일(Configuration file) 또는 Config 파일은 소프트웨어 애플리케이션이나 시스템의 매개변수와 초기 설정을 정의하는 데 사용되는 텍스트 파일입니다.
주요 특징
- 코드와 설정 분리: 설정을 소스 코드와 분리하여 유지보수와 수정을 용이하게 합니다.
- 가독성: 일반적으로 사람이 읽을 수 있는 형식으로 작성되어 쉽게 이해하고 수정할 수 있습니다.
- 유연성: 코드 변경 없이 애플리케이션의 동작을 조정할 수 있습니다.
- 보안 강화: API 키나 데이터베이스 자격 증명과 같은 민감한 정보를 안전하게 저장할 수 있습니다.
구조와 형식
설정 파일의 기본 구조는 다음과 같습니다:
- 키-값 쌍: 설정의 이름(키)과 그 값으로 구성됩니다.
- 섹션과 계층 구조: 큰 설정 파일에서는 설정을 논리적 그룹으로 조직화합니다.
- 주석: 설명이나 부가 정보를 추가할 수 있습니다.
일반적인 파일 형식으로는 YAML, JSON, XML, INI 등이 있으며, 각각의 장단점이 있습니다.
활용
설정 파일은 운영 체제, 인프라 장치, 애플리케이션 등 다양한 IT 환경에서 사용됩니다.
애플리케이션 시작 시 이 파일을 읽어 설정을 적용하며, 일부 설정은 실행 중에도 변경할 수 있습니다.
설정 파일의 효과적인 사용은 애플리케이션의 유연성을 높이고, 배포와 확장을 용이하게 하며, 다양한 환경에서의 커스터마이징을 가능하게 합니다.
JwtConfig와 SecurityConfig는 Spring Security와 JWT를 이용한 인증 시스템에서 중요한 역할을 하는 설정 클래스입니다.
1) JwtConfig
JwtConfig는 JWT(JSON Web Token) 관련 설정을 관리하는 클래스입니다.
주요 기능
- JWT 생성 및 검증에 필요한 비밀 키 관리
- 토큰의 만료 시간 설정
- JWT를 전송할 때 사용할 HTTP 헤더 이름 지정
- 토큰 접두사(일반적으로 "Bearer") 설정
구현 예시
package com.example.foodadmin.config;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.crypto.SecretKey;
import java.security.Key;
import java.util.Date;
@Configuration
public class JwtConfig {
Key key;
// 24시간 유효 토큰 만료 시간 설정 24 * 60 * 60 * 1000
// 1시간 유효 톤큰으로 설정
long tokenValidMillisecond = 1 * 60 * 60 * 1000;
public JwtConfig(@Value("${jwt.secret}") String secretKey) {
this.key = Keys.hmacShaKeyFor(secretKey.getBytes());
}
// 토큰 생성 함수
public String createToken(Long userId) {
// 현재 시간 년월일시분초 가져온다.
Date now = new Date();
// 만료 시간 계산한다.
Date validity = new Date(now.getTime() + tokenValidMillisecond);
return Jwts.builder()
.subject(userId.toString())
.issuedAt(now)
.expiration(validity)
.signWith(key, SignatureAlgorithm.HS256)
.compact(); // 토큰 생성 함수
}
// 토큰에 저장된 데이터 가져오는 함수
public Claims getTokenClaims(String token) {
return Jwts.parser()
.verifyWith((SecretKey) key) // Key 를 SecretKey 로 캐스팅
.build()
.parseSignedClaims(token)
.getPayload();
}
}
2) SecurityConfig
SecurityConfig는 Spring Security의 전반적인 보안 설정을 담당하는 클래스입니다.
주요 기능
- 인증이 필요한 경로와 불필요한 경로 설정
- 로그인 페이지 설정
- CSRF 보호 설정
- JWT 필터 추가
구현 예시
package com.example.foodadmin.config;
import com.example.foodadmin.filter.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
public class SecurityConfig {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
// 암호화 처리 클래스 빈 등록
// PasswordEncoder 는 Spring Security 에서 제공하는 인터페이스로
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/admin/signup",
"/api/v1/admin/login").permitAll()
.anyRequest().authenticated() )
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
2. JwtAuthenticationFilter
JwtAuthenticationFilter는 Spring Security에서 JWT(JSON Web Token)를 이용한 인증을 처리하기 위한 커스텀 필터입니다.
구조 및 동작 원리
- 일반적으로 OncePerRequestFilter 또는 **GenericFilterBean**을 상속받아 구현합니다.
- UsernamePasswordAuthenticationFilter 이전에 실행되도록 설정됩니다.
주요 기능
- 토큰 추출: HTTP 요청의 헤더에서 JWT 토큰을 추출합니다.
- javaString jwt = resolveToken(request);
- 토큰 검증: 추출한 토큰의 유효성을 검사합니다.
- 인증 처리: 유효한 토큰인 경우, 해당 토큰의 인증 정보를 **SecurityContext**에 저장합니다.
- javaSecurityContextHolder.getContext().setAuthentication(authentication);
구현 예시
package com.example.foodadmin.filter;
import com.example.foodadmin.config.JwtConfig;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Date;
@Configuration
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtConfig jwtConfig;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// signup, login 요청은 토큰 검증을 하지 않는다.
if(request.getRequestURI().equals("/api/v1/admin/signup") ||
request.getRequestURI().equals("/api/v1/admin/login") ) {
filterChain.doFilter(request, response);
return;
}
// 헤더에서 토큰을 가져온다.
String bearerToken = request.getHeader("Authorization");
// Bearer 토큰
if (bearerToken == null || bearerToken.isEmpty() ||
!bearerToken.startsWith("Bearer ")) {
response.setStatus(401);
return;
}
// 토큰에서 Bearer 제거 (bearer + 공백 = 7)
String token = bearerToken.substring(7);
// 토큰 유효시간 검증
Claims claims = jwtConfig.getTokenClaims(token);
Date expiration = claims.getExpiration();
if(expiration != null && expiration.before(new Date())) {
response.setStatus(401);
return;
}
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(claims.getSubject(),
null, null);
// SecurityContextHolder 에 인증 정보를 저장한다.
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
설정 방법
Spring Security 설정 클래스에서 다음과 같이 필터를 추가합니다.
javahttp.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
이렇게 구현된 JwtAuthenticationFilter는 클라이언트의 요청마다 JWT 토큰을 검증하고, 유효한 토큰에 대해 인증 정보를 설정하여 보안을 유지합니다.