From 4cbe90d46206f697cfbec8fe760afea0d6c0828b Mon Sep 17 00:00:00 2001 From: hayong39 Date: Sat, 21 Jun 2025 15:38:28 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20security=20config=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../uplait/global/config/SecurityConfig.java | 6 +++++ .../JwtAuthenticationEntryPoint.java | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java diff --git a/src/main/java/com/ureca/uplait/global/config/SecurityConfig.java b/src/main/java/com/ureca/uplait/global/config/SecurityConfig.java index 8743855..1057c2c 100644 --- a/src/main/java/com/ureca/uplait/global/config/SecurityConfig.java +++ b/src/main/java/com/ureca/uplait/global/config/SecurityConfig.java @@ -2,6 +2,7 @@ import com.ureca.uplait.domain.user.repository.UserRepository; import com.ureca.uplait.global.security.jwt.JwtValidator; +import com.ureca.uplait.global.security.jwt.entrypoint.JwtAuthenticationEntryPoint; import com.ureca.uplait.global.security.jwt.filter.JwtAuthenticationFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -40,12 +41,17 @@ public SecurityFilterChain filterChain(HttpSecurity http, UserRepository userRep .csrf(csrf -> csrf.disable()) .cors(cors -> cors.configurationSource(corsConfigurationSource())) .formLogin(form -> form.disable()) + .exceptionHandling(e -> e + .authenticationEntryPoint(new JwtAuthenticationEntryPoint()) + ) .authorizeHttpRequests(auth -> auth .requestMatchers("/auth/login", "/auth/reissue", "/auth/logout").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() .requestMatchers("/health").permitAll() + .requestMatchers("/plan/**").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers(HttpMethod.POST, "/user/extra-info").hasRole("TMP_USER") + .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ) .addFilterBefore(new JwtAuthenticationFilter(jwtValidator, userRepository), UsernamePasswordAuthenticationFilter.class) diff --git a/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java b/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..787a90d --- /dev/null +++ b/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java @@ -0,0 +1,24 @@ +package com.ureca.uplait.global.security.jwt.entrypoint; + +import java.io.IOException; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import com.ureca.uplait.global.security.jwt.JwtProvider; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException, ServletException { + System.out.println("[EntryPoint] 401 Unauthorized 발생 - URI: " + request.getRequestURI()); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } +} From eff5d0f8a71ebad82c8da230eef11f3c4dad9355 Mon Sep 17 00:00:00 2001 From: hayong39 Date: Sat, 21 Jun 2025 15:38:45 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20Reissue=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/jwt/JwtProvider.java | 68 +++++++++++-------- .../jwt/filter/JwtAuthenticationFilter.java | 5 ++ src/main/resources/application.yml | 2 +- 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/ureca/uplait/global/security/jwt/JwtProvider.java b/src/main/java/com/ureca/uplait/global/security/jwt/JwtProvider.java index 070d7d1..f3c0b28 100644 --- a/src/main/java/com/ureca/uplait/global/security/jwt/JwtProvider.java +++ b/src/main/java/com/ureca/uplait/global/security/jwt/JwtProvider.java @@ -5,6 +5,7 @@ import java.util.Date; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseCookie; import org.springframework.stereotype.Component; import com.ureca.uplait.domain.user.entity.User; @@ -57,43 +58,56 @@ public String createRefreshToken(User user) { } public void addAccessTokenCookie(HttpServletResponse response, String token){ - String cookieString = String.format( - "accessToken=%s; Path=/; Max-Age=%d; HttpOnly; Secure=%s; SameSite=%s; Domain=%s", - token, - ACCESS_TOKEN_VALIDITY_SECONDS / 1000, - isSecure ? "Secure" : "", - sameSite, - cookieDomain - ); - response.addHeader("Set-Cookie", cookieString); + ResponseCookie cookie = ResponseCookie.from("accessToken", token) + .path("/") + .httpOnly(true) + .secure(isSecure) + .maxAge(ACCESS_TOKEN_VALIDITY_SECONDS / 1000) + .sameSite(sameSite) + .domain(cookieDomain.isBlank() ? null : cookieDomain) + .build(); + + response.addHeader("Set-Cookie", cookie.toString()); } public void addRefreshTokenCookie(HttpServletResponse response, String token){ - Cookie cookie = new Cookie("refreshToken", token); - cookie.setHttpOnly(true); - cookie.setSecure(true); - cookie.setPath("/"); - cookie.setMaxAge((int) (REFRESH_TOKEN_VALIDITY_SECONDS / 1000)); - response.addCookie(cookie); + ResponseCookie cookie = ResponseCookie.from("refreshToken", token) + .path("/") + .httpOnly(true) + .secure(isSecure) + .maxAge(REFRESH_TOKEN_VALIDITY_SECONDS / 1000) + .sameSite(sameSite) + .domain(cookieDomain.isBlank() ? null : cookieDomain) + .build(); + + response.addHeader("Set-Cookie", cookie.toString()); } public void deleteAccessTokenCookie(HttpServletResponse response){ - String cookieString = String.format( - "accessToken=; Path=/; Max-Age=0; HttpOnly; Secure=%s; SameSite=%s; Domain=%s", - isSecure ? "Secure" : "", - sameSite, - cookieDomain - ); - response.addHeader("Set-Cookie", cookieString); + ResponseCookie cookie = ResponseCookie.from("accessToken", "") + .path("/") + .httpOnly(true) + .secure(isSecure) + .maxAge(0) + .sameSite(sameSite) + .domain(cookieDomain.isBlank() ? null : cookieDomain) + .build(); + + response.addHeader("Set-Cookie", cookie.toString()); } public void deleteRefreshTokenCookie(HttpServletResponse response){ - Cookie cookie = new Cookie("refreshToken", null); - cookie.setMaxAge(0); - cookie.setPath("/"); - cookie.setHttpOnly(true); - response.addCookie(cookie); + ResponseCookie cookie = ResponseCookie.from("refreshToken", "") + .path("/") + .httpOnly(true) + .secure(isSecure) + .maxAge(0) + .sameSite(sameSite) + .domain(cookieDomain.isBlank() ? null : cookieDomain) + .build(); + + response.addHeader("Set-Cookie", cookie.toString()); } public LocalDateTime getRefreshTokenExpiry(String token){ diff --git a/src/main/java/com/ureca/uplait/global/security/jwt/filter/JwtAuthenticationFilter.java b/src/main/java/com/ureca/uplait/global/security/jwt/filter/JwtAuthenticationFilter.java index 0fcf34d..8203b66 100644 --- a/src/main/java/com/ureca/uplait/global/security/jwt/filter/JwtAuthenticationFilter.java +++ b/src/main/java/com/ureca/uplait/global/security/jwt/filter/JwtAuthenticationFilter.java @@ -28,6 +28,11 @@ public JwtAuthenticationFilter(JwtValidator jwtValidator, UserRepository userRep this.userRepository = userRepository; } + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + return request.getRequestURI().equals("/auth/reissue"); + } + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1f9d617..161f00a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -28,7 +28,7 @@ kakao: client-id: ${KAKAO_CLIENT_ID} redirect-uri: ${KAKAO_REDIRECT_URI} jwt: - secret-key: ${JWT_SECRET} + secret: ${JWT_SECRET} access-token-validity: 1800000 refresh-token-validity: 604800000 vector: From 6f806ae28f3af8855b886a74a913a811c2836bfa Mon Sep 17 00:00:00 2001 From: hayong39 Date: Sat, 21 Jun 2025 16:14:59 +0900 Subject: [PATCH 3/6] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=20refreshToken=20DB=EC=97=90=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ureca/uplait/domain/auth/service/AuthService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ureca/uplait/domain/auth/service/AuthService.java b/src/main/java/com/ureca/uplait/domain/auth/service/AuthService.java index e5ea123..7a5549f 100644 --- a/src/main/java/com/ureca/uplait/domain/auth/service/AuthService.java +++ b/src/main/java/com/ureca/uplait/domain/auth/service/AuthService.java @@ -45,7 +45,10 @@ public User handleKakaoLogin(String code, HttpServletResponse response){ tokenRepository.findByUser(user) .ifPresentOrElse( - token -> token.updateRefreshToken(refreshToken, expiryTime), + token -> { + token.updateRefreshToken(refreshToken, expiryTime); + tokenRepository.save(token); + }, () -> tokenRepository.save(Token.builder() .user(user) .refreshToken(refreshToken) From b66de2c7f758baf5d27e786e186e4f766877d90d Mon Sep 17 00:00:00 2001 From: hayong39 Date: Sat, 21 Jun 2025 16:15:22 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/jwt/entrypoint/JwtAuthenticationEntryPoint.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java b/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java index 787a90d..8743de8 100644 --- a/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java +++ b/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java @@ -18,7 +18,6 @@ public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - System.out.println("[EntryPoint] 401 Unauthorized 발생 - URI: " + request.getRequestURI()); response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } } From 0cca7cba53caa9528387a99dcd096fdcd02b90cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EB=8F=99=EC=A4=80?= <127932430+djlim00@users.noreply.github.com> Date: Mon, 23 Jun 2025 08:17:11 +0900 Subject: [PATCH 5/6] Update README.md --- README.md | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 619cae5..91c5996 100644 --- a/README.md +++ b/README.md @@ -94,20 +94,9 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요 -

-# 4. API 명세 -image -image -image -image -image -image -image -

+# 4. 기술 스택 (Tech Stack) -# 5. 기술 스택 (Tech Stack) - -### 5.1 백엔드 +### 4.1 백엔드 | 구분 | 기술 | |------|------| @@ -119,14 +108,14 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요 | 테스트 | ![JUnit5](https://img.shields.io/badge/JUnit%205-25A162?style=flat&logo=jest&logoColor=white)
![Mockito](https://img.shields.io/badge/Mockito-5A6268?style=flat&logo=mockito&logoColor=white) | | DB 마이그레이션 | ![Liquibase](https://img.shields.io/badge/Liquibase-2962FF?style=flat&logo=liquibase&logoColor=white) | -### 5.2 프론트엔드 +### 4.2 프론트엔드 | 구분 | 기술 | |------|------| | 라이브러리 | ![React](https://img.shields.io/badge/React-61DAFB?style=flat&logo=react&logoColor=black) | | HTTP 클라이언트 | ![Axios](https://img.shields.io/badge/Axios-5A29E4?style=flat&logo=axios&logoColor=white) | -### 5.3 AI +### 4.3 AI | 구분 | 기술 | |------|------| @@ -134,14 +123,14 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요 | AI 플랫폼 | ![OpenAI](https://img.shields.io/badge/OpenAI-412991?style=flat&logo=openai&logoColor=white)
![Hugging Face](https://img.shields.io/badge/Hugging%20Face-FFD21F?style=flat&logo=huggingface&logoColor=black) | | LLM 프레임워크 | ![LangChain](https://img.shields.io/badge/LangChain-000000?style=flat&logo=python&logoColor=white) | -### 5.4 데이터베이스 +### 4.4 데이터베이스 | 구분 | 기술 | |------|------| | RDBMS | ![PostgreSQL](https://img.shields.io/badge/PostgreSQL-336791?style=flat&logo=postgresql&logoColor=white) | | 벡터DB 확장 | ![pgvector](https://img.shields.io/badge/pgvector-000000?style=flat&logo=postgresql&logoColor=white) | -### 5.5 협업 & 형상 관리 +### 4.5 협업 & 형상 관리 | 구분 | 기술 | |------|------| @@ -152,7 +141,7 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요 -## 5.6 프로젝트 구조 +## 4.6 프로젝트 구조 - 시스템 아키텍처
![Image](https://github.com/user-attachments/assets/dfb7d2f7-f93f-46d5-a135-7e75038697d5) @@ -160,6 +149,18 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요
![Image](https://github.com/user-attachments/assets/271fbd65-50ba-4528-a96c-3ae0b16a7d34)

+

+ +# 5. API 명세 +image +image +image +image +image +image +image +

+ # 6. 기대 효과 ### 사용자 측면 From 26a4c86462071af2b0c9c463efaed81cb3635bd8 Mon Sep 17 00:00:00 2001 From: Heo JinHyeok Date: Mon, 23 Jun 2025 11:18:01 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=8B=9C=20=EA=B8=88=EC=B9=99=EC=96=B4=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ureca/uplait/domain/review/service/ReviewService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/ureca/uplait/domain/review/service/ReviewService.java b/src/main/java/com/ureca/uplait/domain/review/service/ReviewService.java index e6eb11d..55feaee 100644 --- a/src/main/java/com/ureca/uplait/domain/review/service/ReviewService.java +++ b/src/main/java/com/ureca/uplait/domain/review/service/ReviewService.java @@ -66,6 +66,9 @@ public ReviewCreateResponse createReview(User user, ReviewCreateRequest request) @Transactional public ReviewUpdateResponse updateReview(User user, ReviewUpdateRequest request) { + validateBanWords(request.getTitle()); + validateBanWords(request.getContent()); + Review review = reviewRepository.findById(request.getReviewId()).get(); review.updateReview(