앞으로 과제 하기 전 숙지해둬야할 개념들을 복기 차원에서 정리할 예정이다.
필터(Fillter)

직접 구현할 필요 없이 많이 사용되는 목적에 따라 이미 구현이 되어 있고 우리는 이중에 하나를 선택해서 사용하면 되는데 OncePerRequestFilter 를 제외하고는 거의 사용할 일이 없다.
OncePerRequestFilter
@Component
public class NbcamFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 요청이 들어갈 때 실행되는 부분
System.out.println("✅ NbcamFilter로 들어간다 ");
// 필터 계속 진행
filterChain.doFilter(request, response);
// 요청이 나갈 때 실행되는 부분
System.out.println("✅ NbcamFilter로 나간다 ");
}
}
filterChain.doFilter(request, response); 를 기준으로 필터를 통과해서 들어갈 때와 필터를 통과해서 나갈 때를 구분 짓는다.
JWT
어떤 암호화 알고리즘을 사용할 것인지 ex) HS256
어떤 정보를 넣을 것인지 ex) Tutor, KIM DONG HYUN , male
어떤 암호키로 암호화 할 것인지 ex) Secret Key
만료 시간(리프레쉬 토큰)
JWT 는 최초의 로그인 이후에는 아무런 검증을 하지 않고 무조건 프리패스이다.
편리하지만 그만큼 보안에 약하다.
최소한의 안전 장치로 만료 시간을 저장한다.
implementation "io.jsonwebtoken:jjwt-api:0.12.5"
runtimeOnly "io.jsonwebtoken:jjwt-impl:0.12.5"
runtimeOnly "io.jsonwebtoken:jjwt-jackson:0.12.5" // JSON 직렬화
## 암호화에 사용할 비밀키
jwt:
secret:
key: IfthisgetsstolenitsabigproblemIfthelengthistooshortthesecurityisnotsufficientsoanerroroccurs
package org.example.nbcam_addvanced_1.common.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import java.util.Date;
import javax.crypto.SecretKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class JwtUtil {
public static final String BEARER_PREFIX = "Bearer ";
private static final long TOKEN_TIME = 60 * 60 * 1000L; // 60분
@Value("${jwt.secret.key}") // application.yml 에 있는 key 가져옴
private String secretKeyString;
private SecretKey key;
private JwtParser parser;
/**
* 빈 초기화 메서드
* @PostConstruct 어플리케이션 실행 될 때 가장 먼저 실행 되게 하는 어노테이션
*/
@PostConstruct
public void init() {
byte[] bytes = Decoders.BASE64.decode(secretKeyString);
this.key = Keys.hmacShaKeyFor(bytes);
this.parser = Jwts.parser()
.verifyWith(this.key)
.build();
}
// 토큰 생성
public String generateToken(String username) {
Date now = new Date();
return BEARER_PREFIX + Jwts.builder()
.claim("username", username)
.issuedAt(now)
.expiration(new Date(now.getTime() + TOKEN_TIME))
.signWith(key, Jwts.SIG.HS256)
.compact();
}
// 토큰 검증
public boolean validateToken(String token) {
if (token == null || token.isBlank()) return false;
try {
parser.parseSignedClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
// 개별 예외 분리 없음: 서명/형식/만료 등 모든 실패를 한 번에 처리
log.debug("Invalid JWT: {}", e.toString());
return false;
}
}
// 토큰 복호화
private Claims extractAllClaims(String token) {
return parser.parseSignedClaims(token).getPayload();
}
public String extractUsername(String token) {
return extractAllClaims(token).get("username", String.class);
}
}
@Value("${jwt.secret.key}")
@PostConstruct
claim("username", username)
JWT와 Filter의 조합

그중에서 reqeust 부분에는 위와 같은 그림의 구조로 되어 있다.
Postman 기준 윗쪽 request에 있는 모든 정보를 담고 있고 그것 이외로 Map 타입의 attribute 라는 저장공간이 있어서 attribute 에 JWT 에서 복호화한 정보를 담아서 Controller로 넘겨주는 것이다.
강의를 통해 여러 개념들을 얻게 되었지만, 복기하면서 정리하기에는 방대한 양이라, 하루 하루 과제도 풀어가면서 정리해나갈 예정이다.
용준님~ 용준님의 성장을 항상 응원하며 바라보고 있습니다 😊
짧은 가을이 가고 어느덧 추운 겨울이 다가왔네요~ 이 겨울이 지나고 봄이 올때쯤 용준님의 무한한 성장에 취업길에도 봄이 올거라 믿어 의심치 않습니다~