-
Notifications
You must be signed in to change notification settings - Fork 1
feat: 아이템 메타 데이터 조회 API에 item_top_category 파라미터 추가 #79
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 all commits
ec05e85
7350685
2035d27
789ba68
f008050
f4fa77e
dff8bec
c6a5565
f45ffd9
4ed44fc
0bb8343
27ae4d9
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,27 @@ | ||
| package until.the.eternity.common.entity; | ||
|
|
||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import lombok.Getter; | ||
| import org.springframework.security.web.authentication.WebAuthenticationDetails; | ||
|
|
||
| @Getter | ||
| public class CustomWebAuthenticationDetails extends WebAuthenticationDetails { | ||
|
|
||
| private final String realRemoteAddress; | ||
|
|
||
| public CustomWebAuthenticationDetails(HttpServletRequest request, String realIp) { | ||
| super(request); | ||
| this.realRemoteAddress = realIp; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "CustomWebAuthenticationDetails [RemoteIpAddress(Custom)=" | ||
| + realRemoteAddress | ||
| + ", SessionId=" | ||
| + getSessionId() | ||
| + ", OriginalRemoteAddress(WebDetails)=" | ||
| + super.getRemoteAddress() | ||
| + "]"; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,110 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package until.the.eternity.common.filter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.servlet.FilterChain; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.servlet.ServletException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.servlet.http.HttpServletRequest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.servlet.http.HttpServletResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.ArrayList; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.NonNull; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.core.GrantedAuthority; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.core.context.SecurityContextHolder; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Component; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.filter.OncePerRequestFilter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import until.the.eternity.common.entity.CustomWebAuthenticationDetails; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import until.the.eternity.common.util.IpAddressUtil; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Gateway에서 전달한 인증 헤더(X-Auth-*)를 기반으로 Spring Security의 Authentication을 생성하는 필터 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <p>Gateway에서 전달하는 헤더: - X-Auth-User-Id: 사용자 ID (Long) - X-Auth-Username: 사용자 이메일/username | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * (String) - X-Auth-Roles: 사용자 역할 (예: ROLE_USER, ROLE_ADMIN) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Component | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class GatewayAuthFilter extends OncePerRequestFilter { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final IpAddressUtil ipAddressUtil; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| protected void doFilterInternal( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @NonNull HttpServletRequest request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @NonNull HttpServletResponse response, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @NonNull FilterChain filterChain) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throws ServletException, IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Gateway에서 전달한 인증 헤더 읽기 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String userIdHeader = request.getHeader("X-Auth-User-Id"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String usernameHeader = request.getHeader("X-Auth-Username"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String rolesHeader = request.getHeader("X-Auth-Roles"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UsernamePasswordAuthenticationToken authentication = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getAuthentication(userIdHeader, usernameHeader, rolesHeader); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String clientIp = ipAddressUtil.getClientIp(request); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CustomWebAuthenticationDetails webAuthenticationDetails = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new CustomWebAuthenticationDetails(request, clientIp); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| authentication.setDetails(webAuthenticationDetails); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SecurityContextHolder.getContext().setAuthentication(authentication); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("Authentication set for user: {} with roles: {}", usernameHeader, rolesHeader); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| filterChain.doFilter(request, response); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Gateway에서 전달한 헤더 정보를 기반으로 Authentication 생성 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param userIdHeader 사용자 ID (Gateway에서 검증됨) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param usernameHeader 사용자 이메일/username (Gateway에서 검증됨) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param rolesHeader 사용자 역할 (Gateway에서 검증됨) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return UsernamePasswordAuthenticationToken | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private UsernamePasswordAuthenticationToken getAuthentication( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String userIdHeader, String usernameHeader, String rolesHeader) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // User ID 파싱 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long userId = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (userIdHeader != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| userId = Long.parseLong(userIdHeader); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (NumberFormatException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.warn("Invalid user ID header: {}", userIdHeader); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Roles가 없으면 익명 사용자로 처리 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (rolesHeader == null || rolesHeader.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("No roles found, creating anonymous authentication"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new UsernamePasswordAuthenticationToken(userId, null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+89
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Roles 파싱 (ROLE_USER, ROLE_ADMIN 등) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<GrantedAuthority> authorities = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Gateway에서 넘어온 role이 이미 "ROLE_" prefix를 가지고 있으므로 그대로 사용 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 단, "ROLE_" prefix가 없으면 추가 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String role = rolesHeader.trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!role.startsWith("ROLE_")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role = "ROLE_" + role; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| authorities.add(new SimpleGrantedAuthority(role)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Created authentication for userId: {}, username: {}, role: {}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usernameHeader, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+96
to
+106
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String role = rolesHeader.trim(); | |
| if (!role.startsWith("ROLE_")) { | |
| role = "ROLE_" + role; | |
| } | |
| authorities.add(new SimpleGrantedAuthority(role)); | |
| log.debug( | |
| "Created authentication for userId: {}, username: {}, role: {}", | |
| userId, | |
| usernameHeader, | |
| role); | |
| String[] roles = rolesHeader.split(","); | |
| List<String> parsedRoles = new ArrayList<>(); | |
| for (String r : roles) { | |
| String role = r.trim(); | |
| if (!role.startsWith("ROLE_")) { | |
| role = "ROLE_" + role; | |
| } | |
| authorities.add(new SimpleGrantedAuthority(role)); | |
| parsedRoles.add(role); | |
| } | |
| log.debug( | |
| "Created authentication for userId: {}, username: {}, roles: {}", | |
| userId, | |
| usernameHeader, | |
| parsedRoles); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package until.the.eternity.common.util; | ||
|
|
||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.util.StringUtils; | ||
|
|
||
| @Component | ||
| public class IpAddressUtil { | ||
|
|
||
| public String getClientIp(HttpServletRequest request) { | ||
|
|
||
| String ip = request.getHeader("X-Forwarded-For"); | ||
|
|
||
| if (!isIpFound(ip)) { | ||
| ip = request.getHeader("Proxy-Client-IP"); | ||
| } | ||
| if (!isIpFound(ip)) { | ||
| ip = request.getHeader("WL-Proxy-Client-IP"); | ||
| } | ||
| if (!isIpFound(ip)) { | ||
| ip = request.getHeader("HTTP_CLIENT_IP"); | ||
| } | ||
| if (!isIpFound(ip)) { | ||
| ip = request.getHeader("HTTP_X_FORWARDED_FOR"); | ||
| } | ||
|
|
||
| if (!isIpFound(ip)) { | ||
| ip = request.getRemoteAddr(); | ||
| } | ||
|
|
||
| if (StringUtils.hasText(ip) && ip.contains(",")) { | ||
| return ip.split(",")[0].trim(); | ||
| } | ||
|
|
||
| return ip; | ||
| } | ||
|
|
||
| private boolean isIpFound(String ip) { | ||
| return StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip); | ||
| } | ||
| } |
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.
[nitpick] The Javadoc comment states "Gateway에서 전달한 인증 헤더(X-Auth-*)를 기반으로..." but the actual description format has a line break issue. The description text appears on line 25 without proper spacing from the
<p>tag on line 25.Consider reformatting for better readability: