Fix : 수정

This commit is contained in:
lsh9672 2025-06-12 16:57:34 +09:00
parent 185eafebe6
commit ef87bfbb0b
3 changed files with 154 additions and 75 deletions

View File

@ -1,12 +1,12 @@
package com.ktds.hi.member.config; package com.ktds.hi.member.config;
import com.ktds.hi.member.service.JwtTokenProvider; import com.ktds.hi.member.service.JwtTokenProvider;
import com.ktds.hi.member.service.AuthService;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -19,20 +19,26 @@ import java.io.IOException;
* 요청 헤더의 JWT 토큰을 검증하고 인증 정보를 설정 * 요청 헤더의 JWT 토큰을 검증하고 인증 정보를 설정
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter { public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider; private final JwtTokenProvider tokenProvider;
private final AuthService authService;
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException { FilterChain filterChain) throws ServletException, IOException {
String token = resolveToken(request); try {
String token = resolveToken(request);
if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) { if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
Authentication authentication = tokenProvider.getAuthentication(token); Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
log.debug("JWT 토큰 인증 성공: {}", authentication.getName());
}
} catch (Exception e) {
log.error("JWT 토큰 인증 실패", e);
SecurityContextHolder.clearContext();
} }
filterChain.doFilter(request, response); filterChain.doFilter(request, response);

View File

@ -1,7 +1,8 @@
package com.ktds.hi.member.config; package com.ktds.hi.member.config;
import com.ktds.hi.member.config.JwtAuthenticationFilter;
import com.ktds.hi.member.service.JwtTokenProvider; import com.ktds.hi.member.service.JwtTokenProvider;
import com.ktds.hi.member.service.AuthService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -27,7 +28,6 @@ public class SecurityConfig {
@Qualifier("memberJwtTokenProvider") @Qualifier("memberJwtTokenProvider")
private final JwtTokenProvider jwtTokenProvider; private final JwtTokenProvider jwtTokenProvider;
private final AuthService authService;
/** /**
* 보안 필터 체인 설정 * 보안 필터 체인 설정
@ -40,16 +40,23 @@ public class SecurityConfig {
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authz -> authz .authorizeHttpRequests(authz -> authz
.requestMatchers("/api/auth/**", "/api/members/register").permitAll() .requestMatchers("/api/auth/**", "/api/members/register").permitAll()
.requestMatchers("/swagger-ui/**", "/api-docs/**").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/actuator/**").permitAll() .requestMatchers("/actuator/**").permitAll()
.anyRequest().authenticated() .anyRequest().authenticated()
) )
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, authService), .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
UsernamePasswordAuthenticationFilter.class);
return http.build(); return http.build();
} }
/**
* JWT 인증 필터
*/
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter(jwtTokenProvider);
}
/** /**
* 비밀번호 암호화 * 비밀번호 암호화
*/ */
@ -65,4 +72,45 @@ public class SecurityConfig {
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager(); return config.getAuthenticationManager();
} }
// @Qualifier("memberJwtTokenProvider")
// private final JwtTokenProvider jwtTokenProvider;
// private final AuthService authService;
//
// /**
// * 보안 필터 체인 설정
// * JWT 인증 방식을 사용하고 세션은 무상태로 관리
// */
// @Bean
// public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// http
// .csrf(csrf -> csrf.disable())
// .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// .authorizeHttpRequests(authz -> authz
// .requestMatchers("/api/auth/**", "/api/members/register").permitAll()
// .requestMatchers("/swagger-ui/**", "/api-docs/**").permitAll()
// .requestMatchers("/actuator/**").permitAll()
// .anyRequest().authenticated()
// )
// .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, authService),
// UsernamePasswordAuthenticationFilter.class);
//
// return http.build();
// }
//
// /**
// * 비밀번호 암호화
// */
// @Bean
// public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
// }
//
// /**
// * 인증 매니저
// */
// @Bean
// public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
// return config.getAuthenticationManager();
// }
} }

View File

@ -17,7 +17,7 @@ import java.util.Date;
* JWT 토큰 프로바이더 클래스 * JWT 토큰 프로바이더 클래스
* JWT 토큰 생성, 검증, 파싱 기능을 제공 * JWT 토큰 생성, 검증, 파싱 기능을 제공
*/ */
@Component("memberJwtTokenProvider") // 기존: @Component @Component("memberJwtTokenProvider")
@Slf4j @Slf4j
public class JwtTokenProvider { public class JwtTokenProvider {
@ -25,9 +25,9 @@ public class JwtTokenProvider {
private final long accessTokenExpiration; private final long accessTokenExpiration;
private final long refreshTokenExpiration; private final long refreshTokenExpiration;
public JwtTokenProvider(@Value("${jwt.secret}") String secret, public JwtTokenProvider(@Value("${jwt.secret:mySecretKey123456789012345678901234567890}") String secret,
@Value("${jwt.access-token-expiration}") long accessTokenExpiration, @Value("${jwt.access-token-expiration:1800000}") long accessTokenExpiration,
@Value("${jwt.refresh-token-expiration}") long refreshTokenExpiration) { @Value("${jwt.refresh-token-expiration:604800000}") long refreshTokenExpiration) {
this.secretKey = Keys.hmacShaKeyFor(secret.getBytes()); this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
this.accessTokenExpiration = accessTokenExpiration; this.accessTokenExpiration = accessTokenExpiration;
this.refreshTokenExpiration = refreshTokenExpiration; this.refreshTokenExpiration = refreshTokenExpiration;
@ -41,12 +41,13 @@ public class JwtTokenProvider {
Date expiration = new Date(now.getTime() + accessTokenExpiration); Date expiration = new Date(now.getTime() + accessTokenExpiration);
return Jwts.builder() return Jwts.builder()
.setSubject(memberId.toString()) .setSubject(memberId.toString())
.claim("role", role) .claim("role", role)
.setIssuedAt(now) .claim("type", "access")
.setExpiration(expiration) .setIssuedAt(now)
.signWith(secretKey) .setExpiration(expiration)
.compact(); .signWith(secretKey)
.compact();
} }
/** /**
@ -57,58 +58,82 @@ public class JwtTokenProvider {
Date expiration = new Date(now.getTime() + refreshTokenExpiration); Date expiration = new Date(now.getTime() + refreshTokenExpiration);
return Jwts.builder() return Jwts.builder()
.setSubject(memberId.toString()) .setSubject(memberId.toString())
.setIssuedAt(now) .claim("type", "refresh")
.setExpiration(expiration) .setIssuedAt(now)
.signWith(secretKey) .setExpiration(expiration)
.compact(); .signWith(secretKey)
.compact();
} }
/** /**
* 토큰에서 인증 정보 추출 * 토큰에서 인증 정보 추출
*/ */
public Authentication getAuthentication(String token) { public Authentication getAuthentication(String token) {
Claims claims = parseClaims(token); Claims claims = getClaims(token);
String memberId = claims.getSubject(); String memberId = claims.getSubject();
String role = claims.get("role", String.class); String role = (String) claims.get("role");
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + (role != null ? role : "USER"));
return new UsernamePasswordAuthenticationToken( return new UsernamePasswordAuthenticationToken(
memberId, memberId,
null, null,
Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role)) Collections.singletonList(authority)
); );
} }
/**
* 토큰에서 회원 ID 추출
*/
public Long getMemberIdFromToken(String token) {
Claims claims = parseClaims(token);
return Long.valueOf(claims.getSubject());
}
/** /**
* 토큰 유효성 검증 * 토큰 유효성 검증
*/ */
public boolean validateToken(String token) { public boolean validateToken(String token) {
try { try {
parseClaims(token); getClaims(token);
return true; return true;
} catch (JwtException | IllegalArgumentException e) { } catch (ExpiredJwtException e) {
log.warn("Invalid JWT token: {}", e.getMessage()); log.warn("JWT 토큰 만료: {}", e.getMessage());
return false; } catch (UnsupportedJwtException e) {
log.warn("지원되지 않는 JWT 토큰: {}", e.getMessage());
} catch (MalformedJwtException e) {
log.warn("잘못된 형식의 JWT 토큰: {}", e.getMessage());
} catch (SecurityException | IllegalArgumentException e) {
log.warn("JWT 토큰 검증 실패: {}", e.getMessage());
} }
return false;
} }
/** /**
* 토큰 파싱 * 토큰에서 회원 ID 추출
*/ */
private Claims parseClaims(String token) { public Long getMemberIdFromToken(String token) {
Claims claims = getClaims(token);
return Long.parseLong(claims.getSubject());
}
/**
* 토큰에서 클레임 추출
*/
private Claims getClaims(String token) {
return Jwts.parser() return Jwts.parser()
.setSigningKey(secretKey) .setSigningKey(secretKey)
.build() .build()
.parseClaimsJws(token) .parseClaimsJws(token)
.getBody(); .getBody();
}
/**
* 토큰 만료 시간 조회
*/
public Date getExpirationDateFromToken(String token) {
Claims claims = getClaims(token);
return claims.getExpiration();
}
/**
* 토큰이 만료되었는지 확인
*/
public boolean isTokenExpired(String token) {
Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
} }
} }