diff --git a/README.md b/README.md
index 143df28c6..cdb534384 100755
--- a/README.md
+++ b/README.md
@@ -21,19 +21,10 @@ AnDroid + Blossom
**당신만의 매력적인 이미지**로 **움직이는 귀여운 캐릭터 타임캡슐 스킨**을 직접 제작해보세요.
_우리 앱으로 **과거**와 **현재**, **미래**를 연결하는 특별한 경험을 즐기실 수 있습니다._
-## 🎨 Figma
-### 작업 중...
-
-
-
-
-
-
-
-
-
-
-
+## 🎨 디자인
+
+
+
## 🛠 개발 시스템 구성도
@@ -47,8 +38,8 @@ AnDroid + Blossom
-## ⚙️ 운용 환경
-
+## ⚙️ 운영 환경
+
## 🛢 ERD
@@ -57,5 +48,4 @@ AnDroid + Blossom
ARchive 위키
## 🦾 기술 스택
-
-
+
diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/global/config/security/SecurityConfig.java b/backend/core/src/main/java/site/timecapsulearchive/core/global/config/security/SecurityConfig.java
index 9fd7a8826..f71652fc5 100644
--- a/backend/core/src/main/java/site/timecapsulearchive/core/global/config/security/SecurityConfig.java
+++ b/backend/core/src/main/java/site/timecapsulearchive/core/global/config/security/SecurityConfig.java
@@ -6,7 +6,6 @@
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -15,10 +14,12 @@
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatchers;
import site.timecapsulearchive.core.domain.member.entity.Role;
+import site.timecapsulearchive.core.global.security.filter.DefaultAuthenticationFilter;
@EnableWebSecurity
@Configuration
@@ -28,6 +29,7 @@ public class SecurityConfig {
private final AuthenticationProvider jwtAuthenticationProvider;
private final ObjectMapper objectMapper;
private final AccessDeniedHandler accessDeniedHandler;
+ private final DefaultAuthenticationFilter defaultAuthenticationFilter;
@Bean
public PasswordEncoder getPasswordEncoder() {
@@ -54,6 +56,11 @@ public SecurityFilterChain filterChainWithJwt(final HttpSecurity http) throws Ex
)
.exceptionHandling(error -> error.accessDeniedHandler(accessDeniedHandler));
+ http.addFilterBefore(
+ defaultAuthenticationFilter,
+ UsernamePasswordAuthenticationFilter.class
+ );
+
http.apply(
JwtDsl.jwtDsl(
jwtAuthenticationProvider,
diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/global/error/ErrorCode.java b/backend/core/src/main/java/site/timecapsulearchive/core/global/error/ErrorCode.java
index da198085d..72de9db89 100644
--- a/backend/core/src/main/java/site/timecapsulearchive/core/global/error/ErrorCode.java
+++ b/backend/core/src/main/java/site/timecapsulearchive/core/global/error/ErrorCode.java
@@ -13,6 +13,7 @@ public enum ErrorCode {
INPUT_INVALID_TYPE_ERROR(400, "GLOBAL-003", "잘못된 입력 타입입니다."),
REQUEST_PARAMETER_NOT_FOUND_ERROR(400, "GLOBAL-004", "입력 파라미터가 존재하지 않습니다."),
REQUEST_PARAMETER_TYPE_NOT_MATCH_ERROR(400, "GLOBAL-005", "입력 파라미터의 타입이 올바르지 않습니다."),
+ REQUEST_DEFAULT_KEY_ERROR(400, "GLOBAL-006", "앱에서 발생한 요청이 아닙니다."),
//jwt
INVALID_TOKEN_ERROR(400, "AUTH-001", "jwt 토큰이 유효하지 않습니다."),
diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/global/security/filter/DefaultAuthenticationFilter.java b/backend/core/src/main/java/site/timecapsulearchive/core/global/security/filter/DefaultAuthenticationFilter.java
new file mode 100644
index 000000000..8db5276fa
--- /dev/null
+++ b/backend/core/src/main/java/site/timecapsulearchive/core/global/security/filter/DefaultAuthenticationFilter.java
@@ -0,0 +1,56 @@
+package site.timecapsulearchive.core.global.security.filter;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+import site.timecapsulearchive.core.global.error.ErrorCode;
+import site.timecapsulearchive.core.global.error.ErrorResponse;
+import site.timecapsulearchive.core.global.security.property.DefaultKeyProperties;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DefaultAuthenticationFilter extends OncePerRequestFilter {
+
+ private final DefaultKeyProperties defaultKeyProperties;
+
+ @Override
+ @Order(1)
+ protected void doFilterInternal(
+ HttpServletRequest request,
+ HttpServletResponse response,
+ FilterChain filterChain
+ ) throws ServletException, IOException {
+ String requestKey = request.getHeader("Default-Key");
+
+ if (requestKey == null || !requestKey.equals(defaultKeyProperties.defaultKey())) {
+ log.warn("Invalid default key provided: {}", requestKey);
+
+ final ErrorResponse errorResponse = ErrorResponse.fromErrorCode(
+ ErrorCode.REQUEST_DEFAULT_KEY_ERROR
+ );
+
+ response.setStatus(ErrorCode.REQUEST_DEFAULT_KEY_ERROR.getStatus());
+ response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
+
+ response.getWriter().write(
+ new ObjectMapper().writeValueAsString(
+ errorResponse
+ )
+ );
+
+ return;
+ }
+
+ filterChain.doFilter(request, response);
+ }
+}
diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/global/security/jwt/JwtAuthenticationFilter.java b/backend/core/src/main/java/site/timecapsulearchive/core/global/security/jwt/JwtAuthenticationFilter.java
index 3f6b0387f..f5410ab62 100644
--- a/backend/core/src/main/java/site/timecapsulearchive/core/global/security/jwt/JwtAuthenticationFilter.java
+++ b/backend/core/src/main/java/site/timecapsulearchive/core/global/security/jwt/JwtAuthenticationFilter.java
@@ -8,6 +8,7 @@
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
@@ -32,6 +33,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final RequestMatcher notRequireAuthenticationMatcher;
@Override
+ @Order(2)
protected void doFilterInternal(
final HttpServletRequest request,
final HttpServletResponse response,
diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/global/security/property/DefaultKeyProperties.java b/backend/core/src/main/java/site/timecapsulearchive/core/global/security/property/DefaultKeyProperties.java
new file mode 100644
index 000000000..408bd3938
--- /dev/null
+++ b/backend/core/src/main/java/site/timecapsulearchive/core/global/security/property/DefaultKeyProperties.java
@@ -0,0 +1,10 @@
+package site.timecapsulearchive.core.global.security.property;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "app")
+public record DefaultKeyProperties(
+ String defaultKey
+) {
+
+}
diff --git a/backend/core/src/main/resources/config b/backend/core/src/main/resources/config
index 10bd3becb..92430a653 160000
--- a/backend/core/src/main/resources/config
+++ b/backend/core/src/main/resources/config
@@ -1 +1 @@
-Subproject commit 10bd3becb428fd412e8cec1bcb108b98444a8e3a
+Subproject commit 92430a6534f7b84d7e61429f700723f153fef3fe
diff --git a/backend/core/src/main/resources/logback-spring.xml b/backend/core/src/main/resources/logback-spring.xml
index 4b25a32da..4e3f1d387 100644
--- a/backend/core/src/main/resources/logback-spring.xml
+++ b/backend/core/src/main/resources/logback-spring.xml
@@ -1,34 +1,41 @@
+
- [%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n
-
+ [%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n
-
- ./logs/info.log
-
- INFO
-
-
- [%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n
-
-
-
- ./logs/info.%d{yyyy-MM-dd}.%i.log.gz
-
-
- 100MB
-
- 180
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+ ./logs/info.log
+
+ INFO
+
+
+ [%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n
+
+
+ ./logs/info.%d{yyyy-MM-dd}.%i.log.gz
+
+ 100MB
+
+ 180
+
+
+
+
+
+
+
+
diff --git a/backend/core/src/test/java/site/timecapsulearchive/core/common/config/TestMockMvcSecurityConfig.java b/backend/core/src/test/java/site/timecapsulearchive/core/common/config/TestMockMvcSecurityConfig.java
index 794d15700..b74fa25bd 100644
--- a/backend/core/src/test/java/site/timecapsulearchive/core/common/config/TestMockMvcSecurityConfig.java
+++ b/backend/core/src/test/java/site/timecapsulearchive/core/common/config/TestMockMvcSecurityConfig.java
@@ -8,6 +8,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
+import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
@@ -27,8 +28,10 @@
import site.timecapsulearchive.core.global.api.limit.ApiLimitCheckInterceptor;
import site.timecapsulearchive.core.global.api.limit.ApiLimitProperties;
import site.timecapsulearchive.core.global.api.limit.ApiUsageCacheRepository;
+import site.timecapsulearchive.core.global.security.filter.DefaultAuthenticationFilter;
import site.timecapsulearchive.core.global.security.jwt.JwtAuthenticationFilter;
import site.timecapsulearchive.core.global.security.jwt.JwtAuthenticationProvider;
+import site.timecapsulearchive.core.global.security.property.DefaultKeyProperties;
@EnableWebSecurity
@TestConfiguration
@@ -56,6 +59,7 @@ public SecurityFilterChain filterChainWithJwt(final HttpSecurity http) throws Ex
.anyRequest().hasRole(Role.USER.name())
)
.authenticationProvider(jwtAuthenticationProvider())
+ .addFilterBefore(testDefaultAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(
jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class
@@ -78,6 +82,19 @@ public JwtAuthenticationProvider jwtAuthenticationProvider() {
}
@Bean
+ public DefaultKeyProperties testDefaultKeyProperties() {
+ return new DefaultKeyProperties("testDefaultKey");
+ }
+
+ @Bean
+ @Order(1)
+ public DefaultAuthenticationFilter testDefaultAuthenticationFilter(
+ ) {
+ return new DefaultAuthenticationFilter(testDefaultKeyProperties());
+ }
+
+ @Bean
+ @Order(2)
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter(authenticationManager(), new ObjectMapper(),
notRequireAuthenticationMatcher());
diff --git a/backend/notification/src/main/resources/config b/backend/notification/src/main/resources/config
index 9340cfd0d..9c68d32c6 160000
--- a/backend/notification/src/main/resources/config
+++ b/backend/notification/src/main/resources/config
@@ -1 +1 @@
-Subproject commit 9340cfd0d1b8c12cb4ad8a7b786e7e6f3ed99a18
+Subproject commit 9c68d32c612fb9014eeb75bc8efb01ac495bdd24