-
Notifications
You must be signed in to change notification settings - Fork 0
[MOISAM-246] 유저의 로그인 로그를 저장하여 일별 로그인 유저 수를 어드민에서 확인한다 #172
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
Changes from 3 commits
b053420
4e05867
a614c79
39ec7d2
9d7835f
d7a0131
937e52d
576bb21
8615c6b
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 |
|---|---|---|
|
|
@@ -3,11 +3,14 @@ | |
| import com.meetup.server.auth.dto.CustomOAuth2User; | ||
| import com.meetup.server.auth.support.CookieUtil; | ||
| import com.meetup.server.global.support.jwt.JwtTokenProvider; | ||
| import com.meetup.server.user.domain.type.LoginStatus; | ||
| import com.meetup.server.user.implement.LogUserLoginWriter; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.http.HttpHeaders; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; | ||
| import org.springframework.stereotype.Component; | ||
|
|
@@ -22,6 +25,7 @@ public class OAuth2LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHan | |
|
|
||
| private final JwtTokenProvider tokenProvider; | ||
| private final CookieUtil cookieUtil; | ||
| private final LogUserLoginWriter logUserLoginWriter; | ||
|
|
||
| @Value("${app.oauth2.successRedirectUri}") | ||
| private String successRedirectUri; | ||
|
|
@@ -33,6 +37,10 @@ public void onAuthenticationSuccess( | |
|
|
||
| CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal(); | ||
|
|
||
| String ipAddress = request.getRemoteAddr(); | ||
| String userAgent = request.getHeader(HttpHeaders.USER_AGENT); | ||
| logUserLoginWriter.save(oAuth2User.getUserId(), LoginStatus.SUCCESS, ipAddress, userAgent, null); | ||
|
||
|
|
||
| String accessToken = tokenProvider.createAccessToken(oAuth2User); | ||
| String refreshToken = tokenProvider.createRefreshToken(oAuth2User); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package com.meetup.server.user.domain; | ||
|
|
||
| import com.meetup.server.global.domain.BaseEntity; | ||
| import com.meetup.server.global.util.StringUtil; | ||
| import com.meetup.server.user.domain.type.LoginStatus; | ||
| import jakarta.persistence.*; | ||
| import lombok.AccessLevel; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Entity | ||
| @Table(name = "log_user_login") | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| @Getter | ||
| public class LogUserLogin extends BaseEntity { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| @Column(name = "log_user_login_id") | ||
| private Long id; | ||
|
|
||
| @Column(name = "user_id") | ||
| private Long userId; | ||
|
|
||
| @Enumerated(EnumType.STRING) | ||
| @Column(name = "login_status", nullable = false) | ||
| private LoginStatus loginStatus; | ||
|
|
||
| @Column(name = "ip_address", length = 45) | ||
| private String ipAddress; | ||
|
|
||
| @Column(name = "user_agent") | ||
| private String userAgent; | ||
|
|
||
| @Column(name = "fail_reason") | ||
| private String failReason; | ||
|
|
||
| @Builder | ||
| public LogUserLogin(Long userId, LoginStatus loginStatus, String ipAddress, String userAgent, String failReason) { | ||
| this.userId = userId; | ||
| this.loginStatus = loginStatus; | ||
| this.ipAddress = ipAddress; | ||
| this.userAgent = StringUtil.truncate(userAgent, 255); | ||
| this.failReason = StringUtil.truncate(failReason, 255); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.meetup.server.user.domain.type; | ||
|
|
||
| public enum LoginStatus { | ||
| SUCCESS, | ||
| FAILURE, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.meetup.server.user.implement; | ||
|
|
||
| import com.meetup.server.user.domain.LogUserLogin; | ||
| import com.meetup.server.user.domain.type.LoginStatus; | ||
| import com.meetup.server.user.infrastructure.jpa.LogUserLoginRepository; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.scheduling.annotation.Async; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class LogUserLoginWriter { | ||
|
|
||
| private final LogUserLoginRepository logUserLoginRepository; | ||
|
|
||
| @Async | ||
| public void save(Long userId, LoginStatus loginStatus, String ipAddress, String userAgent, String failReason) { | ||
| logUserLoginRepository.save( | ||
| LogUserLogin.builder() | ||
| .userId(userId) | ||
| .loginStatus(loginStatus) | ||
| .ipAddress(ipAddress) | ||
| .userAgent(userAgent) | ||
| .failReason(failReason) | ||
| .build() | ||
| ); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.meetup.server.user.infrastructure.jpa; | ||
|
|
||
| import com.meetup.server.user.domain.LogUserLogin; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
|
|
||
| public interface LogUserLoginRepository extends JpaRepository<LogUserLogin, Long> { | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
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.
🧩 Analysis chain
🏁 Script executed:
Repository: Team-MOISAM/moisam-server
Length of output: 3723
🏁 Script executed:
Repository: Team-MOISAM/moisam-server
Length of output: 1087
🏁 Script executed:
Repository: Team-MOISAM/moisam-server
Length of output: 3403
🏁 Script executed:
Repository: Team-MOISAM/moisam-server
Length of output: 4233
프록시 환경에서의 IP 주소 추출 처리 필요
request.getRemoteAddr()는 프록시나 로드 밸런서 뒤에서 실행될 때 클라이언트의 실제 IP가 아닌 프록시의 IP를 반환합니다. Docker 배포 환경을 고려할 때X-Forwarded-For또는X-Real-IP헤더를 우선적으로 확인하는 프록시 인식 IP 추출 로직으로 변경하세요. 이 패턴은 OAuth2LoginSuccessHandler에서도 동일하게 나타나므로 두 핸들러 모두 수정이 필요합니다.🤖 Prompt for AI Agents
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.
#172 (comment)
위 방식대로 처리.
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.
🧩 Analysis chain
🏁 Script executed:
Length of output: 648
@anxi01, 이해했습니다! 인프라 레벨에서
externalTrafficPolicy: Local로 설정하여 클라이언트 IP를 보존하는 방식이군요.이 경우
request.getRemoteAddr()를 사용하는 것이 적절합니다. Traefik에서 소스 IP를 보존하므로, 애플리케이션 레벨에서 별도로X-Forwarded-For헤더를 파싱할 필요가 없어 코드가 더 간결해집니다.현재 단일 노드 k3s 환경에서는
Local정책을 사용해도 부하 분산에 문제가 없고, 향후 멀티 노드로 확장할 때 다시 고려하신다는 계획도 합리적입니다. 👍(ㅇㅅㅇ)ノ✨
🧬 Code Graph Analysis Results
filename:src/main/java/com/meetup/server/auth/support/handler/OAuth2LoginFailureHandler.java
filename:src/main/java/com/meetup/server/auth/support/handler/OAuth2LoginSuccessHandler.java
filename:src/main/java/com/meetup/server/user/implement/LogUserLoginWriter.java
filename:src/main/java/com/meetup/server/auth/presentation/filter/JwtAuthenticationFilter.java
filename:src/main/java/com/meetup/server/auth/support/AuthenticationUtil.java
filename:src/main/java/com/meetup/server/global/presentation/ApiControllerAdvice.java
✏️ Learnings added
🧠 Learnings used