From ec4f4f74c0688e02c030f4597c6bdc50a7183281 Mon Sep 17 00:00:00 2001 From: mindol1004 Date: Wed, 25 Sep 2024 17:04:19 +0900 Subject: [PATCH] commit --- .../domain/post/batch/PostCreateBatch.java | 4 ++- .../post/batch/PostCreateBatchChunk.java | 3 ++ .../infra/db/orm/jpa/EntityScanner.java | 3 +- .../infra/security/config/SecurityConfig.java | 6 ++-- .../security/error/SecurityExceptionRule.java | 1 + .../filter/JwtAuthenticationFilter.java | 36 +++++++++---------- .../filter/RedirectIfAuthenticatedFilter.java | 2 +- .../SecurityAuthenticationEntryPoint.java | 23 +++++++++--- .../infra/security/jwt/JwtTokenService.java | 6 ++-- .../static/js/common/axios-instance.js | 9 ++--- .../static/js/pages/dashboard/dashboard.js | 24 ++++++------- .../resources/static/js/pages/sign/sign-in.js | 1 + .../resources/templates/layouts/layout.html | 10 +++--- .../templates/layouts/signin-layout.html | 6 ++-- .../templates/pages/error/error.html | 31 +++++++++++----- 15 files changed, 99 insertions(+), 66 deletions(-) diff --git a/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatch.java b/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatch.java index e29d90f..269bc6e 100644 --- a/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatch.java +++ b/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatch.java @@ -30,7 +30,9 @@ public class PostCreateBatch extends AbstractBatchTask { @Autowired @Override - public void setTransactionManager(@Qualifier(SecondaryJpaConfig.TRANSACTION_MANAGER) PlatformTransactionManager transactionManager) { + public void setTransactionManager( + @Qualifier(SecondaryJpaConfig.TRANSACTION_MANAGER) PlatformTransactionManager transactionManager + ) { super.setTransactionManager(transactionManager); } diff --git a/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatchChunk.java b/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatchChunk.java index c4b5955..4fddfc1 100644 --- a/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatchChunk.java +++ b/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatchChunk.java @@ -42,10 +42,13 @@ import lombok.extern.slf4j.Slf4j; public class PostCreateBatchChunk { private final JobRepository jobRepository; + @Qualifier(SecondaryJpaConfig.TRANSACTION_MANAGER) private final PlatformTransactionManager transactionManager; + @Qualifier(SecondaryJpaConfig.ENTITY_MANAGER_FACTORY) private final EntityManagerFactory entityManagerFactory; + private final PostRepository postRepository; private final PostBackUpRepository postBackUpRepository; diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/EntityScanner.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/EntityScanner.java index 0208fa3..b57dde2 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/EntityScanner.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/EntityScanner.java @@ -6,7 +6,6 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.util.StringUtils; import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; @@ -48,7 +47,7 @@ public class EntityScanner { if (!reader.getAnnotationMetadata().hasAnnotation(Entity.class.getName())) { return false; } - if (StringUtils.hasText(dbName)) { + if (dbName != null) { var attributes = reader.getAnnotationMetadata().getAnnotationAttributes(DatabaseSelector.class.getName()); return attributes != null && dbName.equals(attributes.get("value")); } diff --git a/batch-quartz/src/main/java/com/spring/infra/security/config/SecurityConfig.java b/batch-quartz/src/main/java/com/spring/infra/security/config/SecurityConfig.java index 080f7de..6f4f84d 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/config/SecurityConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/config/SecurityConfig.java @@ -51,7 +51,6 @@ public class SecurityConfig { "/", "/h2-console/**", "/favicon.ico", - "/sign-up", "/api/user/sign-up" }; @@ -95,8 +94,9 @@ public class SecurityConfig { .addFilterAfter( new JwtAuthenticationFilter(tokenService, List.of(PERMITTED_URI)), AuthenticationProcessingFilter.class - ).addFilterAfter( - new RedirectIfAuthenticatedFilter(), + ) + .addFilterAfter( + new RedirectIfAuthenticatedFilter(), JwtAuthenticationFilter.class ) .exceptionHandling(ex -> ex diff --git a/batch-quartz/src/main/java/com/spring/infra/security/error/SecurityExceptionRule.java b/batch-quartz/src/main/java/com/spring/infra/security/error/SecurityExceptionRule.java index ab56dbd..5656499 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/error/SecurityExceptionRule.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/error/SecurityExceptionRule.java @@ -16,6 +16,7 @@ public enum SecurityExceptionRule implements ErrorRule { USER_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "사용자 인증에 실패 하였습니다."), USER_FORBIDDEN(HttpStatus.FORBIDDEN, "사용자 권한이 없습니다."), JWT_TOKEN_ERROR(HttpStatus.UNAUTHORIZED, "토큰이 잘못되었습니다."), + JWT_TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "토큰 정보가 없습니다."), SIGNATURE_ERROR(HttpStatus.UNAUTHORIZED, "토큰이 유효하지 않습니다."), MALFORMED_JWT_ERROR(HttpStatus.UNAUTHORIZED, "올바르지 않은 토큰입니다."), EXPIRED_JWT_ERROR(HttpStatus.UNAUTHORIZED, "토큰이 만료되었습니다. 다시 로그인해주세요."); diff --git a/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java b/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java index 301f836..191bcdb 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java @@ -55,40 +55,36 @@ public final class JwtAuthenticationFilter extends OncePerRequestFilter { ) throws ServletException, IOException { String requestURI = request.getRequestURI(); - if (permitAllUrls.stream().anyMatch(url -> pathMatcher.match(url, requestURI))) { + if (permitAllUrls.stream().anyMatch(url -> pathMatcher.match(url, requestURI)) && !"/".equals(requestURI)) { filterChain.doFilter(request, response); return; } - String accessToken = jwtTokenService.resolveTokenFromCookie(request, JwtTokenRule.ACCESS_PREFIX); - if (jwtTokenService.validateAccessToken(accessToken)) { - setAuthenticationToContext(accessToken); - filterChain.doFilter(request, response); - return; - } + try { + String accessToken = jwtTokenService.resolveTokenFromCookie(request, JwtTokenRule.ACCESS_PREFIX); + if (jwtTokenService.validateAccessToken(accessToken)) { + setAuthenticationToContext(accessToken); + return; + } - String refreshToken = jwtTokenService.resolveTokenFromCookie(request, JwtTokenRule.REFRESH_PREFIX); - if (StringUtils.hasText(refreshToken)) { - if (validateToken(refreshToken, request)) { - try { + String refreshToken = jwtTokenService.resolveTokenFromCookie(request, JwtTokenRule.REFRESH_PREFIX); + if (StringUtils.hasText(refreshToken)) { + if (validateToken(refreshToken, request)) { Authentication authentication = jwtTokenService.getAuthentication(refreshToken); String reissuedAccessToken = jwtTokenService.generateAccessToken(response, authentication); jwtTokenService.generateRefreshToken(response, authentication); setAuthenticationToContext(reissuedAccessToken); - } catch (Exception e) { - jwtTokenService.deleteCookie(response); - request.setAttribute(EXCEPTION_ATTRIBUTE, e); } - filterChain.doFilter(request, response); - return; + } else { + jwtTokenService.deleteCookie(response); } - + } catch (Exception e) { jwtTokenService.deleteCookie(response); - filterChain.doFilter(request, response); - } else { + request.setAttribute(EXCEPTION_ATTRIBUTE, e); + } finally { filterChain.doFilter(request, response); } - + } /** diff --git a/batch-quartz/src/main/java/com/spring/infra/security/filter/RedirectIfAuthenticatedFilter.java b/batch-quartz/src/main/java/com/spring/infra/security/filter/RedirectIfAuthenticatedFilter.java index 42c5dc9..8842285 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/filter/RedirectIfAuthenticatedFilter.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/filter/RedirectIfAuthenticatedFilter.java @@ -23,7 +23,7 @@ public class RedirectIfAuthenticatedFilter extends OncePerRequestFilter { String requestURI = request.getRequestURI(); Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.isAuthenticated() && "/".equals(requestURI)) { - response.sendRedirect("/main"); + response.sendRedirect("/dashboard"); return; } filterChain.doFilter(request, response); diff --git a/batch-quartz/src/main/java/com/spring/infra/security/handler/SecurityAuthenticationEntryPoint.java b/batch-quartz/src/main/java/com/spring/infra/security/handler/SecurityAuthenticationEntryPoint.java index 09519b1..0a75c2c 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/handler/SecurityAuthenticationEntryPoint.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/handler/SecurityAuthenticationEntryPoint.java @@ -7,6 +7,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; @@ -35,12 +37,23 @@ public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoin AuthenticationException authException) throws IOException, ServletException { if (!endpointChecker.isEndpointExist(request)) { response.sendError(HttpServletResponse.SC_NOT_FOUND); + } else if (isApiRequest(request)) { + handleApiRequest(request, response, authException); } else { - if (request.getAttribute(EXCEPTION_ATTRIBUTE) != null) { - resolver.resolveException(request, response, null, (Exception) request.getAttribute(EXCEPTION_ATTRIBUTE)); - } else { - resolver.resolveException(request, response, null, authException); - } + response.sendRedirect("/"); + } + } + + private boolean isApiRequest(HttpServletRequest request) { + String accept = request.getHeader(HttpHeaders.ACCEPT); + return accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE); + } + + private void handleApiRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { + if (request.getAttribute(EXCEPTION_ATTRIBUTE) != null) { + resolver.resolveException(request, response, null, (Exception) request.getAttribute(EXCEPTION_ATTRIBUTE)); + } else { + resolver.resolveException(request, response, null, authException); } } diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java index d59603d..ba31d30 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java @@ -16,6 +16,8 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.spring.infra.security.error.SecurityAuthException; +import com.spring.infra.security.error.SecurityExceptionRule; import com.spring.infra.security.service.UserPrincipalService; import io.jsonwebtoken.Claims; @@ -136,12 +138,12 @@ public class JwtTokenService { * @param request HTTP 요청 객체 * @param tokenPrefix 토큰 접두사 * @return 추출된 토큰 - * @throws IllegalStateException 토큰이 없을 경우 발생 + * @throws SecurityAuthException 토큰이 없을 경우 발생 */ public String resolveTokenFromCookie(HttpServletRequest request, JwtTokenRule tokenPrefix) { Cookie[] cookies = request.getCookies(); if (cookies == null) { - throw new IllegalStateException("JWT_TOKEN_NOT_FOUND"); + throw new SecurityAuthException(SecurityExceptionRule.JWT_TOKEN_NOT_FOUND); } return jwtTokenUtil.resolveTokenFromCookie(cookies, tokenPrefix); } diff --git a/batch-quartz/src/main/resources/static/js/common/axios-instance.js b/batch-quartz/src/main/resources/static/js/common/axios-instance.js index 6693905..e3f3bce 100644 --- a/batch-quartz/src/main/resources/static/js/common/axios-instance.js +++ b/batch-quartz/src/main/resources/static/js/common/axios-instance.js @@ -1,13 +1,15 @@ +const baseUrl = window.BASE_URL || ''; +const timeOut = window.TIME_OUT || 5000; + // Axios apiClient 생성 const apiClient = axios.create({ - baseURL: BASE_URL, - timeout: TIME_OUT, + baseURL: baseUrl, + timeout: timeOut, headers: { 'Content-Type': 'application/json', } }); -// 요청 인터셉터 추가 apiClient.interceptors.request.use( (config) => { return config; @@ -17,7 +19,6 @@ apiClient.interceptors.request.use( } ); -// 응답 인터셉터 추가 apiClient.interceptors.response.use( (response) => { return response.data; diff --git a/batch-quartz/src/main/resources/static/js/pages/dashboard/dashboard.js b/batch-quartz/src/main/resources/static/js/pages/dashboard/dashboard.js index 0f26d9d..dc70abc 100644 --- a/batch-quartz/src/main/resources/static/js/pages/dashboard/dashboard.js +++ b/batch-quartz/src/main/resources/static/js/pages/dashboard/dashboard.js @@ -7,18 +7,6 @@ document.addEventListener('DOMContentLoaded', () => { fetchDataAndRender(); }); -const fetchDataAndRender = async () => { - const [year, month] = selectedMonth.split('-'); - const batchData = await getBatchJobExecutionData(year, month); - const recentJobs = await getRecentJobs(); - - renderBatchExecutionTimeChart(batchData.jobAvgSummary); - renderBatchStatusChart(batchData.statusCounts); - renderHourlyJobExecutionChart(batchData.jobHourSummary); - renderDailyJobExecutionsChart(batchData.jobExecutionSummary); - renderRecentJobsTable(recentJobs); -}; - const initMonthPicker = () => { const monthPicker = document.getElementById('monthPicker'); const currentDate = dayjs(); @@ -36,6 +24,18 @@ const initMonthPicker = () => { }); }; +const fetchDataAndRender = async () => { + const [year, month] = selectedMonth.split('-'); + const batchData = await getBatchJobExecutionData(year, month); + const recentJobs = await getRecentJobs(); + + renderBatchExecutionTimeChart(batchData.jobAvgSummary); + renderBatchStatusChart(batchData.statusCounts); + renderHourlyJobExecutionChart(batchData.jobHourSummary); + renderDailyJobExecutionsChart(batchData.jobExecutionSummary); + renderRecentJobsTable(recentJobs); +}; + const chartOptions = { responsive: true, maintainAspectRatio: false, diff --git a/batch-quartz/src/main/resources/static/js/pages/sign/sign-in.js b/batch-quartz/src/main/resources/static/js/pages/sign/sign-in.js index 528b101..23079d7 100644 --- a/batch-quartz/src/main/resources/static/js/pages/sign/sign-in.js +++ b/batch-quartz/src/main/resources/static/js/pages/sign/sign-in.js @@ -12,6 +12,7 @@ document.addEventListener('DOMContentLoaded', () => { const username = document.getElementById('username').value; const password = document.getElementById('password').value; signIn(username, password).then(response => { + console.log(response); if (response.status) { window.location.href = response.redirectUrl; } diff --git a/batch-quartz/src/main/resources/templates/layouts/layout.html b/batch-quartz/src/main/resources/templates/layouts/layout.html index ab57320..7cee1c8 100644 --- a/batch-quartz/src/main/resources/templates/layouts/layout.html +++ b/batch-quartz/src/main/resources/templates/layouts/layout.html @@ -3,9 +3,9 @@ xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> - -
-
-
- + +
+
+
+ \ No newline at end of file diff --git a/batch-quartz/src/main/resources/templates/layouts/signin-layout.html b/batch-quartz/src/main/resources/templates/layouts/signin-layout.html index 2211103..cf37b27 100644 --- a/batch-quartz/src/main/resources/templates/layouts/signin-layout.html +++ b/batch-quartz/src/main/resources/templates/layouts/signin-layout.html @@ -3,7 +3,7 @@ xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> - -
- + +
+ \ No newline at end of file diff --git a/batch-quartz/src/main/resources/templates/pages/error/error.html b/batch-quartz/src/main/resources/templates/pages/error/error.html index 527f8a3..39b633b 100644 --- a/batch-quartz/src/main/resources/templates/pages/error/error.html +++ b/batch-quartz/src/main/resources/templates/pages/error/error.html @@ -1,12 +1,27 @@ - + - - Error page - + Error - -

Error Page

- - + +
+
+
+
+
+ +
+

500

+

내부 서버 오류

+

죄송합니다. 문제가 발생했습니다. 기술팀이 이 문제를 해결하기 위해 노력하고 있습니다.

+ + 홈으로 돌아가기 + +
+
+
+
+ \ No newline at end of file