Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 앱 요청 식별 필터 추가 #654

Merged
merged 9 commits into from
Aug 29, 2024
24 changes: 7 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,10 @@ An<u>Droid</u> + <u>Blossom</u> </h2>
<br>**당신만의 매력적인 이미지**로 **움직이는 귀여운 캐릭터 타임캡슐 스킨**을 직접 제작해보세요.
<br>_우리 앱으로 **과거**와 **현재**, **미래**를 연결하는 특별한 경험을 즐기실 수 있습니다._

## 🎨 Figma
### 작업 중...
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/0666eae8-fca0-4f01-8bbd-57a82b988a5d" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/755eee1f-5d0d-4219-addd-e82865183c65" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/89d71e74-1790-42e7-878b-2c5ef354229b" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/1553ac60-466c-4bf8-b2ef-4cb43001ee2e" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/8d1ec9e0-9993-4379-9324-a69ab6c19ed6" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/82c76117-14f0-4621-9638-47e5142256f6" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/4d1f7a0a-14ea-4762-81ab-3063c953a120" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/fe886e4d-7d59-417e-8bd8-156e493a97a9" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/f32c9b95-3b4b-4109-93e8-0f4969fe283c" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/2d595cb4-8654-4a33-b024-da59b1451e9a" width="190px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/66acc665-0f44-4de7-ac0e-421f5ecefc47" width="190px" height="400px">
## 🎨 디자인
<img src="https://github.com/user-attachments/assets/f93e805b-749d-4128-b2ce-b32df4a8fb29" width="230px" height="440px">
<img src="https://github.com/user-attachments/assets/d39044b2-2a6e-4974-b769-26a7cbd3ab21" width="230px" height="440px">
<img src="https://github.com/user-attachments/assets/5042e84e-6642-41a1-82c2-ced5303069bf" width="230px" height="440px">

## 🛠 개발 시스템 구성도
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/1932a1c6-d8f2-4c11-82f4-1a602852bcd3">
Expand All @@ -47,8 +38,8 @@ An<u>Droid</u> + <u>Blossom</u> </h2>
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/f7e3625c-389b-4a7b-95b7-b56117309e79" width="260px" height="350px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/68e6f0f5-5281-469e-b637-d343ac7fcfc7" width="260px" height="350px">

## ⚙️ 운용 환경
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/243bd68b-80f4-47ae-872c-a0e921c211f4">
## ⚙️ 운영 환경
<img src="https://github.com/user-attachments/assets/fcba2bff-1807-41d6-b4e2-2db8e240c4e5">

## 🛢 ERD
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/59e02146-a89c-4a71-b31a-debef38cb30d">
Expand All @@ -57,5 +48,4 @@ An<u>Droid</u> + <u>Blossom</u> </h2>
<a href="https://github.com/tukcomCD2024/DroidBlossom/wiki" style="font-size: 25px">ARchive 위키</a>

## 🦾 기술 스택
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/10a4a325-7e95-43eb-877c-c8e6f81e9d91" width="400px" height="400px">
<img src="https://github.com/tukcomCD2024/DroidBlossom/assets/68144059/ce1b280f-622f-48bb-8f1d-7ad05732f533" width="400px" height="400px">
<img src="https://github.com/user-attachments/assets/2a68bbc1-bdb9-456d-b3f7-abcbd5d8c619">
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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() {
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 토큰이 유효하지 않습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
) {

}
2 changes: 1 addition & 1 deletion backend/core/src/main/resources/config
63 changes: 35 additions & 28 deletions backend/core/src/main/resources/logback-spring.xml
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 콘솔 출력 설정 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n
</Pattern>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<appender name="INFO_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/info.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./logs/info.%d{yyyy-MM-dd}.%i.log.gz
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>180</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<logger name="org.hibernate" level="error" additivity="false">
<appender-ref ref="INFO_LOG"/>
</logger>
<appender-ref ref="INFO_LOG"/>
</root>
</configuration>

<!-- local 프로파일일 때 콘솔에만 로그 출력 -->
<springProfile name="local">
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</springProfile>

<!-- local 프로파일이 아닐 때 로그를 파일로 출력 -->
<springProfile name="!local">
<appender name="INFO_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/info.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./logs/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>180</maxHistory>
</rollingPolicy>
</appender>

<root level="INFO">
<appender-ref ref="INFO_LOG" />
</root>
</springProfile>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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());
Expand Down
2 changes: 1 addition & 1 deletion backend/notification/src/main/resources/config
Loading