diff --git a/cakey-api/src/main/java/com/cakey/common/Constant.java b/cakey-api/src/main/java/com/cakey/common/Constant.java new file mode 100644 index 0000000..f32ad01 --- /dev/null +++ b/cakey-api/src/main/java/com/cakey/common/Constant.java @@ -0,0 +1,5 @@ +package com.cakey.common; + +public abstract class Constant { + public static final String CHARACTER_TYPE = "utf-8"; +} \ No newline at end of file diff --git a/cakey-api/src/main/java/com/cakey/common/filter/RequiredAuthenticationFilter.java b/cakey-api/src/main/java/com/cakey/common/filter/RequiredAuthenticationFilter.java index 3e8fb1e..2d022f9 100644 --- a/cakey-api/src/main/java/com/cakey/common/filter/RequiredAuthenticationFilter.java +++ b/cakey-api/src/main/java/com/cakey/common/filter/RequiredAuthenticationFilter.java @@ -1,21 +1,27 @@ package com.cakey.common.filter; +import com.cakey.common.Constant; +import com.cakey.common.response.ApiResponseUtil; import com.cakey.jwt.auth.JwtProvider; import com.cakey.jwt.auth.UserAuthentication; import com.cakey.jwt.auth.JwtValidationType; import com.cakey.rescode.ErrorBaseCode; import com.cakey.rescode.ErrorCode; import com.cakey.user.exception.UserBadRequestException; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.PrintWriter; import java.util.List; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; @@ -24,6 +30,7 @@ @RequiredArgsConstructor public class RequiredAuthenticationFilter extends OncePerRequestFilter { private final JwtProvider jwtProvider; //로그인 필수 + private final ObjectMapper objectMapper; // 필터를 건너뛸 API 경로 목록 private static final List EXCLUDED_PATHS = List.of( @@ -63,13 +70,25 @@ protected void doFilterInternal( ) throws ServletException, IOException { try { final String token = getAccessTokenFromCookie(request); - final Long userId = jwtProvider.getUserIdFromSubject(token); SecurityContextHolder.getContext().setAuthentication(new UserAuthentication(userId, null, null)); + filterChain.doFilter(request, response); // 다음 필터로 요청 전달 } catch (Exception e) { - throw new UserBadRequestException(ErrorBaseCode.UNAUTHORIZED); + // 예외 발생 시 JSON 응답 생성 + final ErrorBaseCode errorCode = ErrorBaseCode.UNAUTHORIZED; + + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(Constant.CHARACTER_TYPE); + response.setStatus(errorCode.getHttpStatus().value()); // HTTP 상태 코드 401 설정 + + // `ApiResponseUtil.failure`를 이용해 응답 작성 + final PrintWriter writer = response.getWriter(); + writer.write(objectMapper.writeValueAsString( + ApiResponseUtil.failure(errorCode).getBody() + )); + writer.flush(); + return; // 체인 호출 중단 } - filterChain.doFilter(request, response); } private String getAccessTokenFromCookie(final HttpServletRequest request) throws Exception { diff --git a/cakey-api/src/main/java/com/cakey/user/exception/UserErrorCode.java b/cakey-api/src/main/java/com/cakey/user/exception/UserErrorCode.java index 436ced2..28f238a 100644 --- a/cakey-api/src/main/java/com/cakey/user/exception/UserErrorCode.java +++ b/cakey-api/src/main/java/com/cakey/user/exception/UserErrorCode.java @@ -8,10 +8,6 @@ @RequiredArgsConstructor public enum UserErrorCode implements ErrorCode { - /** - * 404 Not Found - */ - /** * 404 Not Found diff --git a/cakey-api/src/main/java/com/cakey/user/service/UserService.java b/cakey-api/src/main/java/com/cakey/user/service/UserService.java index 4911e02..9e8ea2b 100644 --- a/cakey-api/src/main/java/com/cakey/user/service/UserService.java +++ b/cakey-api/src/main/java/com/cakey/user/service/UserService.java @@ -147,7 +147,7 @@ public void deleteRefreshCookie(HttpServletResponse response) { public void setAccessCookie(final String accessToken, final HttpServletResponse response) { ResponseCookie accessCookie = ResponseCookie.from(ACCESS_TOKEN, accessToken) - .maxAge(14* 24 * 60 * 60 * 1000L) //액세스 토큰 기간 2주 + .maxAge(30 * 24 * 60 * 60 * 1000L) /// 1달 .path("/") .secure(true) .sameSite("None") @@ -158,7 +158,7 @@ public void setAccessCookie(final String accessToken, final HttpServletResponse public void setRefreshCookie(final String refreshToken, final HttpServletResponse response) { ResponseCookie refreshCookie = ResponseCookie.from(REFRESH_TOKEN, refreshToken) - .maxAge(14* 24 * 60 * 60 * 1000L) //리프레시 토큰 기간 2주 + .maxAge(30 * 24 * 60 * 60 * 1000L) /// 1달 .path("/") .secure(true) .sameSite("None") diff --git a/cakey-auth/src/main/java/com/cakey/jwt/auth/JwtGenerator.java b/cakey-auth/src/main/java/com/cakey/jwt/auth/JwtGenerator.java index 064c409..7f861c3 100644 --- a/cakey-auth/src/main/java/com/cakey/jwt/auth/JwtGenerator.java +++ b/cakey-auth/src/main/java/com/cakey/jwt/auth/JwtGenerator.java @@ -1,5 +1,6 @@ package com.cakey.jwt.auth; +import com.cakey.jwt.domain.Token; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Header; import io.jsonwebtoken.Jws; @@ -18,16 +19,12 @@ @RequiredArgsConstructor @Component public class JwtGenerator { - - //todo: 추후에 properties로 가져오기(데로 참고) - private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 14* 24 * 60 * 60 * 1000L; //2시간 - private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 14* 24 * 60 * 60 * 1000L; //2주 - private static final String SECRET_KEY = "cakeyfsdafasdfjsadrhksadrhskdlrskadjlralsdkrhasdklrhsadr"; + private final JwtProperties jwtProperties; //액세스 토큰 발급 public String generateAccessToken(final long userId) { final Date now = new Date(); - final Date expireDate = generateExpirationDate(now); + final Date expireDate = generateExpirationDate(now, true); return Jwts.builder() .setHeaderParam(Header.TYPE, Header.JWT_TYPE) @@ -41,7 +38,7 @@ public String generateAccessToken(final long userId) { @Cacheable(value = "refresh") public String generateRefreshToken(final long userId) { final Date now = new Date(); - final Date expireDate = generateExpirationDate(now); + final Date expireDate = generateExpirationDate(now, false); return Jwts.builder() .setHeaderParam(Header.TYPE, Header.JWT_TYPE) @@ -52,8 +49,12 @@ public String generateRefreshToken(final long userId) { .compact(); } - private Date generateExpirationDate(final Date now) { - return new Date(now.getTime() + ACCESS_TOKEN_EXPIRATION_TIME); + private Date generateExpirationDate(final Date now, final boolean isAccessToken) { + if (isAccessToken) { + return new Date(now.getTime() + jwtProperties.getAccessTokenExpirationTime()); + } else { + return new Date(now.getTime() + jwtProperties.getRefreshTokenExpirationTime()); + } } public Key getSigningKey() { @@ -61,7 +62,7 @@ public Key getSigningKey() { } private String encodeSecretKeyToBase64() { - return Base64.getEncoder().encodeToString(SECRET_KEY.getBytes()); + return Base64.getEncoder().encodeToString(jwtProperties.getSecret().getBytes()); } public Jws parseToken(final String token) { diff --git a/cakey-auth/src/main/java/com/cakey/jwt/auth/JwtProperties.java b/cakey-auth/src/main/java/com/cakey/jwt/auth/JwtProperties.java new file mode 100644 index 0000000..b4d4256 --- /dev/null +++ b/cakey-auth/src/main/java/com/cakey/jwt/auth/JwtProperties.java @@ -0,0 +1,17 @@ +package com.cakey.jwt.auth; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Getter +@Setter +@ConfigurationProperties("jwt") +@Component +public class JwtProperties { + private String secret; + private long accessTokenExpirationTime; + private long refreshTokenExpirationTime; + +} diff --git a/cakey-common/src/main/java/com/cakey/rescode/ErrorBaseCode.java b/cakey-common/src/main/java/com/cakey/rescode/ErrorBaseCode.java index 4718c6e..97851cd 100644 --- a/cakey-common/src/main/java/com/cakey/rescode/ErrorBaseCode.java +++ b/cakey-common/src/main/java/com/cakey/rescode/ErrorBaseCode.java @@ -17,7 +17,6 @@ public enum ErrorBaseCode implements ErrorCode { BAD_REQUEST_MISSING_PARAM(HttpStatus.BAD_REQUEST, 40003, "필수 param이 없습니다."), BAD_REQUEST_METHOD_ARGUMENT_TYPE(HttpStatus.BAD_REQUEST, 40004, "메서드 인자타입이 잘못되었습니다."), BAD_REQUEST_NOT_READABLE(HttpStatus.BAD_REQUEST, 40005, "json 오류 혹은 reqeust body 필드 오류 입니다."), - BAD_REQUEST_ENUM_VALUE(HttpStatus.BAD_REQUEST, 40006, "잘못된 enum 값입니다."), /** * 401 Unauthorized