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 명세
-
-
-
-
-
-
-
-
+# 4. 기술 스택 (Tech Stack)
-# 5. 기술 스택 (Tech Stack)
-
-### 5.1 백엔드
+### 4.1 백엔드
| 구분 | 기술 |
|------|------|
@@ -119,14 +108,14 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요
| 테스트 | 
 |
| DB 마이그레이션 |  |
-### 5.2 프론트엔드
+### 4.2 프론트엔드
| 구분 | 기술 |
|------|------|
| 라이브러리 |  |
| HTTP 클라이언트 |  |
-### 5.3 AI
+### 4.3 AI
| 구분 | 기술 |
|------|------|
@@ -134,14 +123,14 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요
| AI 플랫폼 | 
 |
| LLM 프레임워크 |  |
-### 5.4 데이터베이스
+### 4.4 데이터베이스
| 구분 | 기술 |
|------|------|
| RDBMS |  |
| 벡터DB 확장 |  |
-### 5.5 협업 & 형상 관리
+### 4.5 협업 & 형상 관리
| 구분 | 기술 |
|------|------|
@@ -152,7 +141,7 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요
-## 5.6 프로젝트 구조
+## 4.6 프로젝트 구조
- 시스템 아키텍처

@@ -160,6 +149,18 @@ LLM 기반 챗봇과의 대화를 통해 사용자는 자신에게 알맞는 요

+
+
+# 5. API 명세
+
+
+
+
+
+
+
+
+
# 6. 기대 효과
### 사용자 측면
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)
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(
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/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/entrypoint/JwtAuthenticationEntryPoint.java b/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java
new file mode 100644
index 0000000..8743de8
--- /dev/null
+++ b/src/main/java/com/ureca/uplait/global/security/jwt/entrypoint/JwtAuthenticationEntryPoint.java
@@ -0,0 +1,23 @@
+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 {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+}
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 3a10a53..52f4d1d 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -45,7 +45,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: