[D&X:W] conference 프로젝트를 진행중이라, 개발을 진행하였음
소셜 로그인(구글) 부분을 맡았고, JWT 토큰 구현 완료하였음
package conference.clerker.global.jwt;
import conference.clerker.global.oauth2.service.CustomUserDetailsService;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.ServletException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@RequiredArgsConstructor
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
private final CustomUserDetailsService customUserDetailsService; // CustomUserDetailsService가 주입됩니다.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
try {
String jwt = getJwtFromRequest(request);
if (jwt != null && jwtProvider.validateToken(jwt)) {
String email = jwtProvider.getEmailFromToken(jwt);
UserDetails userDetails = customUserDetailsService.loadUserByUsername(email); // 사용자 정보 가져오기
if (userDetails != null) {
Authentication authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
// 인증 정보 설정
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
} catch (ExpiredJwtException e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
package conference.clerker.global.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class JwtProvider {
@Value("${jwt.secret-key}")
private String secretKey;
@Value("${jwt.access-token.expiration}")
private long tokenValidityInSeconds;
private Key getSigningKey() {
return Keys.hmacShaKeyFor(secretKey.getBytes());
}
// JWT 토큰 생성 (추가 정보 포함)
public String generateToken(Map<String, Object> additionalClaims) {
Date now = new Date();
long jwtExpirationMs = TimeUnit.HOURS.toMillis(tokenValidityInSeconds); // 시간을 밀리초로 변환
Date expiryDate = new Date(now.getTime() + jwtExpirationMs);
return Jwts.builder()
.setIssuedAt(now)
.setExpiration(expiryDate)
.addClaims(additionalClaims) // 추가 정보 (custom claims) 설정
.signWith(getSigningKey(), SignatureAlgorithm.HS512)
.compact();
}
// 이메일 추출
public String getEmailFromToken(String token) {
return parseClaims(token).get("email", String.class); // email 키로 이메일 추출
}
// 사용자 이름 추출
public String getUsernameFromToken(String token) {
return parseClaims(token).get("username", String.class); // username 키로 사용자 이름 추출
}
// 토큰에서 Claims 추출
private Claims parseClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
// 토큰 유효성 검증
public boolean validateToken(String token) {
try {
Claims claims = parseClaims(token);
return !claims.getExpiration().before(new Date());
} catch (Exception e) {
log.error("Invalid JWT token: ", e);
return false;
}
}
}
개발을 하면서 느끼는 건, 혼자만의 힘으로는 모든 것을 해결하기 어렵다는 점이다. 특히 내가 구현하려는 로직이나 워크 플로우에 대해 깊이 이해하고 있지 않다면, 그 과정이 더더욱 복잡하고 어려워진다. 다행히 GPT와 같은 도구의 도움을 받으면서 문제를 하나하나 해결할 수 있었고, 이를 통해 개발을 조금씩 앞으로 나아가고 있다.
이번에 JWT 구현을 성공적으로 마무리하면서 정말 큰 성취감을 느꼈다. 처음에는 막연하고 복잡하게만 느껴졌던 JWT도, 차근차근 단계를 밟아가며 해결해나가니 어느새 하나의 완성된 기능이 되어 있었다. 물론, 쉽지 않은 여정이었지만, 그만큼 보람도 크다. 또한, 이렇게 개발에만 집중할 수 있는 시간을 가질 수 있다는 것에 큰 감사함을 느낀다. 바쁜 일상 속에서도 내가 몰두할 수 있는 시간이 있다는 것은 생각보다 소중한 경험이다. 오늘 하루는 그런 의미에서 정말 뜻깊은 하루였다. 앞으로도 이런 시간을 소중히 여기며, 더 많은 성장을 이루고 싶다.