Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b7a7e9c
feat(#21): 로그아웃 API 엔드포인트 구현
lgw323 Sep 15, 2025
3a167c8
docs(#21): 로그아웃 API 명세 추가
lgw323 Sep 15, 2025
892732e
feat(#21): 로그아웃 관련 예외 코드 추가
lgw323 Sep 15, 2025
09d90db
feat(#21): 로그아웃 성공 코드 추가
lgw323 Sep 15, 2025
2f8427a
feat(#21): 로그아웃 서비스 로직 기본 골격 구현
lgw323 Sep 15, 2025
b0d1f5e
feat(#21): Authorization 헤더 토큰 추출 기능 구현
lgw323 Sep 15, 2025
0bd4441
feat(#21): 토큰에서 추출한 정보 받아오기 추가
lgw323 Sep 15, 2025
399adce
feat(#21): 토큰정보 해체 후 분석기능 추가
lgw323 Sep 15, 2025
48f72e1
chore(#21): Redis 의존성 추가
lgw323 Sep 17, 2025
9fa9b67
docs(#21): 로그아웃 API 명세 최신화
lgw323 Sep 17, 2025
c055ad3
feat(#21): 로그아웃 관련 예외 코드 최신화
lgw323 Sep 17, 2025
a080a30
feat(#21): 로그아웃 서비스 액세스 토큰 Redis 블랙리스트 처리 구현
lgw323 Sep 17, 2025
a8bd809
feat(#21): 토큰 예외 처리 최신화
lgw323 Sep 17, 2025
7664881
feat(#21): 로그아웃 API 엔드포인트 구현
lgw323 Sep 15, 2025
8d7a0d7
docs(#21): 로그아웃 API 명세 추가
lgw323 Sep 15, 2025
2d28a33
feat(#21): 로그아웃 관련 예외 코드 추가
lgw323 Sep 15, 2025
9dcb648
feat(#21): 로그아웃 성공 코드 추가
lgw323 Sep 15, 2025
78cc3b4
feat(#21): 로그아웃 서비스 로직 기본 골격 구현
lgw323 Sep 15, 2025
97c5f77
feat(#21): 토큰에서 추출한 정보 받아오기 추가
lgw323 Sep 15, 2025
8fbb7e0
feat(#21): 토큰정보 해체 후 분석기능 추가
lgw323 Sep 18, 2025
e94d67b
chore(#21): Redis 의존성 추가
lgw323 Sep 17, 2025
a1079c9
docs(#21): 로그아웃 API 명세 최신화
lgw323 Sep 17, 2025
f9e22d8
feat(#21): 로그아웃 서비스 액세스 토큰 Redis 블랙리스트 처리 구현
lgw323 Sep 17, 2025
48f7845
feat(#21): 토큰 예외 처리 최신화
lgw323 Sep 18, 2025
bc958e1
chore(#21): SecurityConfig에 /auth/logout 경로 인증 설정 추가
lgw323 Sep 24, 2025
16bbb13
refactor(#21): 헤더 문자열에서 직접 토큰을 추출하는 메소드 추가
lgw323 Sep 24, 2025
083a8ec
refactor(#21): Claims 추출 로직 수정 및 테스트용 토큰 생성 메소드 추가
lgw323 Sep 24, 2025
86bddae
feat(#21): 로그아웃 관련 예외 코드 정의 수정
lgw323 Sep 24, 2025
8b56458
feat(#21): Redis를 이용한 로그아웃 서비스 로직 구현 (블랙리스트 추가, RT 삭제)
lgw323 Sep 24, 2025
2c12c05
feat(#21): 로그아웃 API 엔드포인트 구현
lgw323 Sep 24, 2025
b1ae1ca
docs(#21): 로그아웃 API SpringDoc 명세 수정
lgw323 Sep 24, 2025
5bce66e
test(#21): 로그아웃 서비스 통합 테스트 코드 작성
lgw323 Sep 24, 2025
1711dbd
test(#21): 로그아웃 API 컨트롤러 테스트 코드 작성
lgw323 Sep 24, 2025
6fbb581
feat(#21): 로그인 및 로그아웃 기능 병합 충돌 해결
lgw323 Sep 24, 2025
74811e2
feat(#21): 로그인 및 로그아웃 기능 병합 충돌 해결
lgw323 Sep 24, 2025
6dd4c7d
refactor(#21): 액세스 및 리프레시 토큰의 시크릿 키 분리
lgw323 Sep 25, 2025
145f9a5
refactor(#21): 액세스 및 리프레시 토큰의 시크릿 키 분리
lgw323 Sep 25, 2025
323dc51
refactor(#21): 액세스 및 리프레시 토큰의 시크릿 키 분리
lgw323 Sep 25, 2025
4d084f6
fix(#21): 필수 헤더 누락 시 500 대신 400 에러를 반환하도록 수정
lgw323 Sep 25, 2025
5a23071
refactor(#21): 로그아웃 시 토큰 블랙리스트 등록 로직 간소화
lgw323 Sep 25, 2025
4718698
refactor(#21): 로그아웃 로직 테스트 보강
lgw323 Sep 25, 2025
b7189bf
test(#21): JwtTokenResolver 토큰 추출 로직 테스트 추가
lgw323 Sep 25, 2025
38dd656
refactor(#21): 로그아웃시 DB 조회 로직 추가
lgw323 Sep 25, 2025
26511a2
test(#21): 로그아웃시 DB 조회 테스트 로직 추가
lgw323 Sep 25, 2025
8bc7856
refactor(#21): 멤버 파라미터 추가
lgw323 Oct 1, 2025
c4a294a
docs(#21): 멤버 파라미터 추가
lgw323 Oct 1, 2025
399a43d
refactor(#21): 미사용 예외 제거
lgw323 Oct 1, 2025
3d89dd9
refactor(#21): 책임 간소화 및 멤버 파라미터 추가
lgw323 Oct 1, 2025
55a324d
refactor(#21): 경로 수정
lgw323 Oct 1, 2025
a0a8743
feat(#21): 블랙리스트 담당 분리
lgw323 Oct 1, 2025
a02a603
chore(#21): jwt 설정값 분리
lgw323 Oct 1, 2025
165217c
refactor(#21): jwt 생성/검증 분리
lgw323 Oct 1, 2025
09d0140
feat(#21): 토큰 생성/검증 bean 생성
lgw323 Oct 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
Expand Down

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헤더로 액세스 토큰을 받는 것과 더불어 @IdentifiedUser 어노테이션을 사용해 Member 객체 또한 받는 것이 좋을 것 같습니다.

현재 로직으로는 인증 필터 체인을 거치지 않고 컨트롤러 단으로 요청이 바로 전달되기 때문에, 서비스 로직에서 토큰 추출, 인증, 관리까지 모두 담당하게 되어 책임이 지나치게 무거워집니다.

구현되어 있는 인증 필터 체인을 사용해서 토큰 추출 및 인증에 대한 책임을 필터 체인에 인계하고, 서비스 로직에서는 토큰 블랙리스트 관리만 담당하게 하는 것이 좋은 코드가 될 것 같습니다~!

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import kr.ac.ks.cs_web_back.domain.auth.dto.request.AuthLoginRequest;
import kr.ac.ks.cs_web_back.domain.auth.dto.response.AuthLoginResponse;
import kr.ac.ks.cs_web_back.domain.auth.service.AuthService;
import kr.ac.ks.cs_web_back.domain.member.model.Member;
import kr.ac.ks.cs_web_back.global.annotation.IdentifiedUser;
import kr.ac.ks.cs_web_back.global.response.CsResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
Expand All @@ -23,4 +25,13 @@ public CsResponse<AuthLoginResponse> login(
AuthLoginResponse token = authService.loginMember(request);
return CsResponse.of(AuthSuccessCode.LOGIN_SUCCESS, token);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드 사이 한줄 띄워줘요


@PostMapping("/logout")
public CsResponse<Void> logout (
@Valid @RequestHeader("Authorization") String authorization,
@IdentifiedUser Member member
) {
authService.logout(authorization, member.getEmail());
return CsResponse.of(AuthSuccessCode.LOGOUT_SUCCESS);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package kr.ac.ks.cs_web_back.domain.auth.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.ac.ks.cs_web_back.domain.auth.dto.request.AuthLoginRequest;
import kr.ac.ks.cs_web_back.domain.auth.dto.response.AuthLoginResponse;
import kr.ac.ks.cs_web_back.domain.member.model.Member;
import kr.ac.ks.cs_web_back.global.annotation.IdentifiedUser;
import kr.ac.ks.cs_web_back.global.response.CsResponse;
import kr.ac.ks.cs_web_back.global.swagger.error.ApiErrorResponse;
import kr.ac.ks.cs_web_back.global.swagger.error.ErrorCase;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestHeader;

@Tag(name = "인증 API", description = "인증 관련 API")
public interface SpringDocAuthController {
Expand All @@ -30,4 +34,17 @@ CsResponse<AuthLoginResponse> login(
@RequestBody AuthLoginRequest request
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 한줄 띄워줘요

@Operation(summary = "로그아웃", description = "사용자의 JWT토큰을 받아 형식을 검사하고 성공시 토큰을 만료시킵니다.")
@ApiResponse(responseCode = "200", description = "로그아웃에 성공했습니다.")
@ApiErrorResponse(status = HttpStatus.UNAUTHORIZED, instance = "/auth/logout", errorCases = {
@ErrorCase(description = "유효하지 않은 토큰", code = 8001, exampleMessage = "유효하지 않은 토큰입니다."),
@ErrorCase(description = "인증 실패", code = 8002, exampleMessage = "인증에 실패했습니다.")
})
@ApiErrorResponse(status = HttpStatus.INTERNAL_SERVER_ERROR, instance = "/auth/logout", errorCases = {
@ErrorCase(description = "서버 오류", code = 5000, exampleMessage = "서버에서 예기치 못한 오류가 발생했습니다.")
})
CsResponse<Void> logout(
@RequestHeader("Authorization") String authorizationHeader,
@Parameter(hidden=true)@IdentifiedUser Member member
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ public enum AuthExceptionCode implements ExceptionCode {

BAD_REQUEST_NO_EMAIL(9010, "이메일이 입력되지 않았습니다."),
BAD_REQUEST_NO_PASSWORD(9010, "비밀번호가 입력되지 않았습니다."),

UNAUTHORIZED_PASSWORD(8011, "이메일 또는 비밀번호가 일치하지 않습니다."),
UNAUTHORIZED_INVALID_TOKEN(8001, "유효하지 않은 토큰입니다."),
UNAUTHORIZED_FAILED_VALIDATION(8002, "인증에 실패했습니다."),
UNAUTHORIZED_PASSWORD(8011, "비밀번호가 일치하지 않습니다."),
;

private final int code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
public enum AuthSuccessCode implements SuccessCode {

LOGIN_SUCCESS(2001, "로그인에 성공했습니다."),
LOGOUT_SUCCESS(2002, "로그아웃에 성공했습니다."),
;

private final int code;
Expand Down

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthController 리뷰에서 언급한 필터 체인 사용과 별개로,
각 메서드가 토큰 발급 혹은 인증과 레디스 토큰 관리를 모두 담당하고 있어 단일 책임 원칙 (SRP)에 부합하지 않은 설계라고 보여집니다.
Redis 조작 로직을 별도의 메서드로 분리하거나, 더욱 책임을 분산시키기 위해서 별도의 클래스로 작성하여 레디스 관련 로직을 모두 위임하는 방법도 좋을 것 같습니다.

Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,69 @@
import kr.ac.ks.cs_web_back.domain.auth.dto.response.AuthLoginResponse;
import kr.ac.ks.cs_web_back.domain.member.model.Member;
import kr.ac.ks.cs_web_back.domain.member.repository.MemberRepository;
import kr.ac.ks.cs_web_back.global.exeption.domain.InvalidTokenException;
import kr.ac.ks.cs_web_back.global.exeption.domain.NotFoundException;
import kr.ac.ks.cs_web_back.global.exeption.domain.UnauthorizedException;
import kr.ac.ks.cs_web_back.global.jwt.JwtTokenResolver;
import kr.ac.ks.cs_web_back.global.jwt.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
@Transactional
public class AuthService {

private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
private final RedisTemplate<String, String> redisTemplate;
private final JwtUtil jwtUtil;
private final JwtTokenResolver jwtTokenResolver;
private final JwtBlackListService jwtBlackListService;


public AuthLoginResponse loginMember(AuthLoginRequest request) {
Member member = memberRepository.findByEmail(request.email())
.orElseThrow(() -> new NotFoundException(AuthExceptionCode.UNAUTHORIZED_FAILED_VALIDATION));

if(!passwordEncoder.matches(request.password(), member.getPassword()))
throw new UnauthorizedException(AuthExceptionCode.UNAUTHORIZED_PASSWORD);
// 24 hours

String accessToken = jwtUtil.generateAccessToken(member.getEmail());
String refreshToken = jwtUtil.generateRefreshToken(member.getEmail());

Date refreshTokenExpiration = jwtUtil.getExpirationDateFromRefreshToken(refreshToken);
long remainingTime = refreshTokenExpiration.getTime() - System.currentTimeMillis();

redisTemplate.opsForValue().set(
"RT:"+ member.getEmail(),
refreshToken,
remainingTime,
TimeUnit.MILLISECONDS
);

return AuthLoginResponse.builder()
.authorization(accessToken)
.authorizationRefresh(refreshToken)
.build();
}
public void logout(String authorization, String email) {
String resolvedAccessToken = jwtTokenResolver.resolveToken(authorization);

if (jwtBlackListService.isBlackListed(resolvedAccessToken))
throw new InvalidTokenException(AuthExceptionCode.UNAUTHORIZED_FAILED_VALIDATION);

if (redisTemplate.opsForValue().get("RT:"+email) != null)
redisTemplate.delete("RT:"+email);

Date expirationTime = jwtUtil.getExpirationDateFromAccessToken(resolvedAccessToken);
long remainingTime = expirationTime.getTime() - System.currentTimeMillis();
jwtBlackListService.blacklist(resolvedAccessToken, remainingTime);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package kr.ac.ks.cs_web_back.domain.auth.service;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class JwtBlackListService {

private final RedisTemplate<String, String> redisTemplate;

public void blacklist(String accessToken, long remainingTime) {
redisTemplate.opsForValue().set(
accessToken,
"logout",
remainingTime,
TimeUnit.MILLISECONDS
);
}

public boolean isBlackListed(String accessToken) {
return redisTemplate.opsForValue().get(accessToken) != null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authz -> authz
.requestMatchers(PathRequest.toH2Console()).permitAll() // H2 콘솔 경로 허용
.requestMatchers("/member/register", "/auth/login", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/member/register", "/auth/login", "/auth/logout", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
Expand Down

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Authorization 헤더가 누락된 요청을 받는 경우 컨트롤러까지 요청이 전달되지 않고 Jwt 인증 필터 체인에서 예외를 발생하기 때문에 컨트롤러에서 발생시키는 헤더를 핸들링하는 GlobalExceptionHandler에는 메서드가 필요하지 않을 것으로 보입니다

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

Expand Down Expand Up @@ -88,4 +89,13 @@ public ResponseEntity<ExceptionResponse> handleException(final ConflictException
.body(new ExceptionResponse(exception.getCode(), exception.getMessage()));
}

@ExceptionHandler(MissingRequestHeaderException.class)
public ResponseEntity<ExceptionResponse> handleException(final MissingRequestHeaderException e) {
System.out.printf("%s : %s\n", e.getClass(), e.getMessage());

final int errorCode = 400;
final String errorMessage = "필수 헤더가 누락되었습니다: " + e.getHeaderName();
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ExceptionResponse(errorCode, errorMessage));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected void doFilterInternal(
if (token != null) {
jwtUtil.validateToken(token);

String email = jwtUtil.getEmailFromToken(token);
String email = jwtUtil.getEmailFromAccessToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/kr/ac/ks/cs_web_back/global/jwt/JwtConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package kr.ac.ks.cs_web_back.global.jwt;

import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
@PropertySource("classpath:env.properties")
@RequiredArgsConstructor
public class JwtConfig {

private final JwtProperties jwtProperties;

@Bean(name = "accessTokenProvider")
public TokenProvider accessTokenProvider() {
return new TokenProvider(jwtProperties.accessSecret(), jwtProperties.accessTokenExpireTime());
}

@Bean(name = "refreshTokenProvider")
public TokenProvider refreshTokenProvider() {
return new TokenProvider(jwtProperties.refreshSecret(), jwtProperties.refreshTokenExpireTime());
}
}
12 changes: 12 additions & 0 deletions src/main/java/kr/ac/ks/cs_web_back/global/jwt/JwtProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package kr.ac.ks.cs_web_back.global.jwt;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "jwt")
public record JwtProperties(
String accessSecret,
String refreshSecret,
Long accessTokenExpireTime,
Long refreshTokenExpireTime
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,10 @@ public String resolveToken(HttpServletRequest request) {
return null;
}

public String resolveToken(String authorizationHeader) {
if(StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith(HEADER_PREFIX))
return authorizationHeader.substring(HEADER_PREFIX.length());

return null;
}
}
58 changes: 40 additions & 18 deletions src/main/java/kr/ac/ks/cs_web_back/global/jwt/JwtUtil.java

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JwtUtil의 책임이 당초 설계할 때 예상했던 것 보다 점점 무거워지는 것 같아요
추후에 JwtUtil 클래스의 책임을 분산하는 방안에 대해서 고민해보면 좋을 것 같습니다!

Original file line number Diff line number Diff line change
Expand Up @@ -14,54 +14,76 @@

@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.accessSecret}")
private String aSecret;

@Value("${jwt.refreshSecret}")
private String rSecret;

@Value("${jwt.accessTokenExpireTime}")
private Long accessTokenExpireTime;

@Value("${jwt.refreshTokenExpireTime}")
private Long refreshTokenExpireTime;

private Key hmacKey;
private Key hmacAccessKey;
private Key hmacRefreshKey;

@PostConstruct
public void init() {
byte[] decodedKey = Base64.getDecoder().decode(secret);
this.hmacKey = new SecretKeySpec(decodedKey, SignatureAlgorithm.HS256.getJcaName());
}
public String generateAccessToken(String email) {
return generateToken(email, accessTokenExpireTime);
}
byte[] accessDecodedKey = Base64.getDecoder().decode(aSecret);
this.hmacAccessKey = new SecretKeySpec(accessDecodedKey, SignatureAlgorithm.HS256.getJcaName());

public String generateRefreshToken(String email) {
return generateToken(email, refreshTokenExpireTime);
byte[] refreshDecodedKey = Base64.getDecoder().decode(rSecret);
this.hmacRefreshKey = new SecretKeySpec(refreshDecodedKey, SignatureAlgorithm.HS256.getJcaName());
}

private String generateToken(String email, Long expireTime) {
private String generateToken(String email, Long expireTime, Key key) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expireTime);
return Jwts.builder().setSubject(email)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(hmacKey)
.signWith(key)
.compact();
}

public String getEmailFromToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(hmacKey)
public String generateAccessToken(String email) {
return generateToken(email, accessTokenExpireTime, hmacAccessKey);
}

public String generateRefreshToken(String email) {
return generateToken(email, refreshTokenExpireTime, hmacRefreshKey);
}

public String generateTestToken(String email, Long expireTime) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generateTestToken 제거해 주세요!
테스트 때문에 실제 서비스 로직에 필요 없는 코드가 추가되면 안됩니다

return generateToken(email, expireTime, hmacAccessKey);
}

private Claims getAllClaimsFromToken(String token, Key key) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
}

public Date getExpirationDateFromAccessToken(String token) {
return getAllClaimsFromToken(token, hmacAccessKey).getExpiration();
}

public Date getExpirationDateFromRefreshToken(String token) {
return getAllClaimsFromToken(token, hmacRefreshKey).getExpiration();
}

return claims.getSubject();
public String getEmailFromAccessToken(String token) {
return getAllClaimsFromToken(token, hmacAccessKey).getSubject();
}

public void validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(hmacKey)
.setSigningKey(hmacAccessKey)
.build()
.parseClaimsJws(token);
} catch (ExpiredJwtException e) {
Expand Down
Loading
Loading