-
Notifications
You must be signed in to change notification settings - Fork 0
[feat] 알림센터 저장 기능 #298
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
[feat] 알림센터 저장 기능 #298
Changes from all commits
d1e4cc4
c926863
a5f53b2
1dd9ac3
fb61586
4d1d316
cd10cdb
ec18391
28d1bb0
7a62726
47215b3
9f28bd6
3f0918e
4b2d4ff
4b34ea3
90b8495
bb8d52b
597e8cf
f5ceff7
48bacfb
983abaf
05256bb
3f9abb9
b07e0a5
bed7c40
64728a7
30bc7c9
a2102fa
af159e1
4c68d4c
6850a50
7a05757
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package konkuk.thip.common.security.constant; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Getter | ||
| public enum SecurityWhitelist { | ||
|
|
||
| SWAGGER_UI("/swagger-ui/**"), | ||
| API_DOCS("/api-docs/**"), | ||
| SWAGGER_UI_HTML("/swagger-ui.html"), | ||
| V3_API_DOCS("/v3/api-docs/**"), | ||
| OAUTH2_AUTHORIZATION("/oauth2/authorization/**"), | ||
| LOGIN_OAUTH2_CODE("/login/oauth2/code/**"), | ||
| ACTUATOR_HEALTH("/actuator/health"), | ||
| AUTH_USERS("/auth/users"), | ||
| AUTH_TOKEN("/auth/token"), | ||
| API_TEST("/api/test/**"), | ||
| AUTH_EXCHANGE_TEMP_TOKEN("/auth/exchange-temp-token"), | ||
| AUTH_SET_COOKIE("/auth/set-cookie"); | ||
|
|
||
| private final String pattern; | ||
|
|
||
| // SecurityConfig 용 전체 리스트 | ||
| public static String[] patterns() { | ||
| return Arrays.stream(values()) | ||
| .map(SecurityWhitelist::getPattern) | ||
| .toArray(String[]::new); | ||
| } | ||
|
|
||
| // JwtAuthenticationFilter.shouldNotFilter() 용 편의 메서드 | ||
| public static List<String> patternsList() { | ||
| return Arrays.asList(patterns()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,29 @@ | ||
| package konkuk.thip.common.security.filter; | ||
|
|
||
| import jakarta.annotation.PostConstruct; | ||
| import jakarta.servlet.FilterChain; | ||
| import jakarta.servlet.ServletException; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import konkuk.thip.common.exception.AuthException; | ||
| import konkuk.thip.common.security.constant.SecurityWhitelist; | ||
| import konkuk.thip.common.security.oauth2.CustomOAuth2User; | ||
| import konkuk.thip.common.security.oauth2.LoginUser; | ||
| import konkuk.thip.common.security.util.JwtUtil; | ||
| import konkuk.thip.user.application.port.UserTokenBlacklistQueryPort; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.http.server.PathContainer; | ||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.filter.OncePerRequestFilter; | ||
| import org.springframework.web.util.pattern.PathPattern; | ||
| import org.springframework.web.util.pattern.PathPatternParser; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
|
|
||
| import static konkuk.thip.common.exception.code.ErrorCode.*; | ||
| import static konkuk.thip.common.security.constant.AuthParameters.*; | ||
|
|
@@ -30,6 +36,31 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { | |
| private final JwtUtil jwtUtil; | ||
| private final UserTokenBlacklistQueryPort userTokenBlacklistQueryPort; | ||
|
|
||
| private List<PathPattern> whitelistPatterns; | ||
| private final PathPatternParser pathPatternParser = new PathPatternParser(); | ||
|
|
||
| @PostConstruct | ||
| void initWhitelistPatterns() { | ||
| this.whitelistPatterns = SecurityWhitelist.patternsList().stream() | ||
| .map(pathPatternParser::parse) // 애플리케이션 시작 시 1회 컴파일 | ||
| .toList(); | ||
| } | ||
|
|
||
| @Override | ||
| protected boolean shouldNotFilter(HttpServletRequest request) { | ||
| // 컨텍스트 패스 고려한 실제 경로(= path) 추출 | ||
| String requestUri = request.getRequestURI(); | ||
| String contextPath = request.getContextPath(); | ||
| String path = (contextPath != null && !contextPath.isEmpty() && requestUri.startsWith(contextPath)) | ||
| ? requestUri.substring(contextPath.length()) | ||
| : requestUri; | ||
|
|
||
| PathContainer container = PathContainer.parsePath(path); | ||
|
|
||
| // PathPattern으로 세그먼트 기반 매칭 | ||
| return whitelistPatterns.stream().anyMatch(p -> p.matches(container)); | ||
| } | ||
|
Comment on lines
+42
to
+62
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 OOME를 피하고자 처음 시작시에만 정적으로 리스트를 만들어둔 건가요??
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스프링 서버가 처음 뜰 때 한번만(= @PostConstruct) enum 으로 관리하는 whitelist 들을 PathPattern 객체로 변환하여 shouldNotFilter 메서드에서 계속 사용하는 것을 의도한 코드이긴 합니다!!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네넵 좋습니다! Pattern 객체를 매 패턴 매치시에 사용하면 OOME가 발생할 수도 있는 문제는 것으로 알아서 성준님 코드처럼 미리 컴파일 시점에 객체를 만들어두고 사용하는 것이 안정성이 더 높은 것으로 알고 있습니다. 참고 블로그입니다! https://blogshine.tistory.com/687
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 좋은 레퍼런스 공유 감사합니닷 |
||
|
|
||
| @Override | ||
| protected void doFilterInternal(HttpServletRequest request, | ||
| HttpServletResponse response, | ||
|
|
@@ -92,24 +123,4 @@ private String extractToken(HttpServletRequest request) { | |
| log.info("토큰이 없습니다."); | ||
| return null; | ||
| } | ||
|
|
||
| @Override | ||
| protected boolean shouldNotFilter(HttpServletRequest request) { | ||
| String path = request.getRequestURI(); | ||
|
|
||
| // 화이트리스트 경로에 대해서는 JWT 필터 제외 | ||
| return path.startsWith("/swagger-ui") | ||
| || path.startsWith("/v3/api-docs") | ||
| || path.startsWith("/api-docs") | ||
| || path.startsWith("/actuator/health") | ||
| || path.startsWith("/oauth2/authorization") | ||
| || path.startsWith("/login/oauth2/code") | ||
| || path.startsWith("/auth/users") | ||
| || path.equals("/auth/token") | ||
|
|
||
| // || path.equals("/auth/set-cookie") | ||
| // || path.equals("/auth/exchange-temp-token") | ||
| ; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,18 +13,26 @@ public class FeedEventPublisherAdapter implements FeedEventCommandPort { | |
| private final ApplicationEventPublisher publisher; | ||
|
|
||
| @Override | ||
| public void publishFollowEvent(Long targetUserId, Long actorUserId, String actorUsername) { | ||
| public void publishFollowEvent( | ||
| String title, String content, | ||
| Long targetUserId, Long actorUserId, String actorUsername) { | ||
| publisher.publishEvent(FeedEvents.FollowerEvent.builder() | ||
| .title(title) | ||
| .content(content) | ||
| .targetUserId(targetUserId) | ||
| .actorUserId(actorUserId) | ||
| .actorUsername(actorUsername) | ||
| .build()); | ||
| } | ||
|
Comment on lines
+16
to
26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확인했습니다~ |
||
|
|
||
| @Override | ||
| public void publishFeedCommentedEvent(Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| public void publishFeedCommentedEvent( | ||
| String title, String content, | ||
| Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| publisher.publishEvent(FeedEvents.FeedCommentedEvent.builder() | ||
| .title(title) | ||
| .content(content) | ||
| .targetUserId(targetUserId) | ||
| .actorUserId(actorUserId) | ||
| .actorUsername(actorUsername) | ||
|
|
@@ -33,9 +41,13 @@ public void publishFeedCommentedEvent(Long targetUserId, Long actorUserId, Strin | |
| } | ||
|
|
||
| @Override | ||
| public void publishFeedRepliedEvent(Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| public void publishFeedRepliedEvent( | ||
| String title, String content, | ||
| Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| publisher.publishEvent(FeedEvents.FeedCommentRepliedEvent.builder() | ||
| .title(title) | ||
| .content(content) | ||
| .targetUserId(targetUserId) | ||
| .actorUserId(actorUserId) | ||
| .actorUsername(actorUsername) | ||
|
|
@@ -44,9 +56,13 @@ public void publishFeedRepliedEvent(Long targetUserId, Long actorUserId, String | |
| } | ||
|
|
||
| @Override | ||
| public void publishFolloweeNewPostEvent(Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| public void publishFolloweeNewPostEvent( | ||
| String title, String content, | ||
| Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| publisher.publishEvent(FeedEvents.FolloweeNewPostEvent.builder() | ||
| .title(title) | ||
| .content(content) | ||
| .targetUserId(targetUserId) | ||
| .actorUserId(actorUserId) | ||
| .actorUsername(actorUsername) | ||
|
|
@@ -55,9 +71,13 @@ public void publishFolloweeNewPostEvent(Long targetUserId, Long actorUserId, Str | |
| } | ||
|
|
||
| @Override | ||
| public void publishFeedLikedEvent(Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| public void publishFeedLikedEvent( | ||
| String title, String content, | ||
| Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| publisher.publishEvent(FeedEvents.FeedLikedEvent.builder() | ||
| .title(title) | ||
| .content(content) | ||
| .targetUserId(targetUserId) | ||
| .actorUserId(actorUserId) | ||
| .actorUsername(actorUsername) | ||
|
|
@@ -66,9 +86,13 @@ public void publishFeedLikedEvent(Long targetUserId, Long actorUserId, String ac | |
| } | ||
|
|
||
| @Override | ||
| public void publishFeedCommentLikedEvent(Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| public void publishFeedCommentLikedEvent( | ||
| String title, String content, | ||
| Long targetUserId, Long actorUserId, String actorUsername, | ||
| Long feedId) { | ||
| publisher.publishEvent(FeedEvents.FeedCommentLikedEvent.builder() | ||
| .title(title) | ||
| .content(content) | ||
| .targetUserId(targetUserId) | ||
| .actorUserId(actorUserId) | ||
| .actorUsername(actorUsername) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enum으로 따로 분리하셧꾼요 좋네요 👍🏻👍🏻