From cbfd43e4959bd81ec52233dfff27d998b241da3e Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Mon, 3 Mar 2025 18:57:47 +0900 Subject: [PATCH 01/19] =?UTF-8?q?hotfix:=20cors=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=ED=95=9C=20=EB=B6=80=EB=B6=84=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95=20[PR?= =?UTF-8?q?BE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/olive/pribee/global/config/SecurityConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/olive/pribee/global/config/SecurityConfig.java b/src/main/java/com/olive/pribee/global/config/SecurityConfig.java index 1faab4f..befa59a 100644 --- a/src/main/java/com/olive/pribee/global/config/SecurityConfig.java +++ b/src/main/java/com/olive/pribee/global/config/SecurityConfig.java @@ -37,6 +37,7 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth From 965d22255f6f7ea9d678bc78cef933479b4c7ac7 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Mon, 3 Mar 2025 19:12:24 +0900 Subject: [PATCH 02/19] =?UTF-8?q?hotfix:=20oauth2=EC=9D=98=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20cors=20=EC=97=90=20=EC=A0=81=EC=9A=A9=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/olive/pribee/global/config/SecurityConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/olive/pribee/global/config/SecurityConfig.java b/src/main/java/com/olive/pribee/global/config/SecurityConfig.java index befa59a..2c647cd 100644 --- a/src/main/java/com/olive/pribee/global/config/SecurityConfig.java +++ b/src/main/java/com/olive/pribee/global/config/SecurityConfig.java @@ -74,6 +74,7 @@ public CorsConfigurationSource corsConfigurationSource() { // 특정 API 경로에 대해 CORS 정책을 적용 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/api/**", configuration); + source.registerCorsConfiguration("/oauth2/**", configuration); source.registerCorsConfiguration("/swagger-ui/**", configuration); source.registerCorsConfiguration("/v3/api-docs/**", configuration); source.registerCorsConfiguration("/webjars/**", configuration); From 98f117820dbb943b2eedec3ac661e08b785dde18 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Mon, 3 Mar 2025 21:28:08 +0900 Subject: [PATCH 03/19] =?UTF-8?q?hotfix:=20=EC=84=A4=EC=A0=95=ED=95=9C=20c?= =?UTF-8?q?ors=20security=EC=97=90=20=EC=84=A4=EC=A0=95=ED=95=98=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/olive/pribee/global/config/SecurityConfig.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/olive/pribee/global/config/SecurityConfig.java b/src/main/java/com/olive/pribee/global/config/SecurityConfig.java index 2c647cd..b0e2865 100644 --- a/src/main/java/com/olive/pribee/global/config/SecurityConfig.java +++ b/src/main/java/com/olive/pribee/global/config/SecurityConfig.java @@ -37,7 +37,6 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth From 17e41945912a59730fd1f1a782613fc773e3a29c Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 14:39:46 +0900 Subject: [PATCH 04/19] =?UTF-8?q?remove:=20security=20oauth=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/domain/entity/CustomOAuth2User.java | 30 ----------- .../OAuth2AuthenticationFailureHandler.java | 27 ---------- .../OAuth2AuthenticationSuccessHandler.java | 44 ---------------- .../auth/service/CustomOAuth2UserService.java | 52 ------------------- 4 files changed, 153 deletions(-) delete mode 100644 src/main/java/com/olive/pribee/module/auth/domain/entity/CustomOAuth2User.java delete mode 100644 src/main/java/com/olive/pribee/module/auth/handler/OAuth2AuthenticationFailureHandler.java delete mode 100644 src/main/java/com/olive/pribee/module/auth/handler/OAuth2AuthenticationSuccessHandler.java delete mode 100644 src/main/java/com/olive/pribee/module/auth/service/CustomOAuth2UserService.java diff --git a/src/main/java/com/olive/pribee/module/auth/domain/entity/CustomOAuth2User.java b/src/main/java/com/olive/pribee/module/auth/domain/entity/CustomOAuth2User.java deleted file mode 100644 index 57447ce..0000000 --- a/src/main/java/com/olive/pribee/module/auth/domain/entity/CustomOAuth2User.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.olive.pribee.module.auth.domain.entity; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.core.user.OAuth2User; - -public record CustomOAuth2User( - Member member, - Map attributes -) implements OAuth2User { - - @Override - public Map getAttributes() { - return attributes; - } - - @Override - public Collection getAuthorities() { - return Collections.singleton(new SimpleGrantedAuthority(member.getRole().name())); - } - - @Override - public String getName() { - return member.getFacebookId(); - } -} diff --git a/src/main/java/com/olive/pribee/module/auth/handler/OAuth2AuthenticationFailureHandler.java b/src/main/java/com/olive/pribee/module/auth/handler/OAuth2AuthenticationFailureHandler.java deleted file mode 100644 index 45b6647..0000000 --- a/src/main/java/com/olive/pribee/module/auth/handler/OAuth2AuthenticationFailureHandler.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.olive.pribee.module.auth.handler; - -import java.io.IOException; - -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import org.springframework.stereotype.Component; - -import com.olive.pribee.global.util.ResponseUtil; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Component -@RequiredArgsConstructor -@Slf4j -public class OAuth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { - - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, - AuthenticationException exception) throws IOException { - log.error(exception.getMessage()); - ResponseUtil.setDataResponse(response, HttpServletResponse.SC_UNAUTHORIZED, exception.getMessage()); - } -} diff --git a/src/main/java/com/olive/pribee/module/auth/handler/OAuth2AuthenticationSuccessHandler.java b/src/main/java/com/olive/pribee/module/auth/handler/OAuth2AuthenticationSuccessHandler.java deleted file mode 100644 index 5c5ba8b..0000000 --- a/src/main/java/com/olive/pribee/module/auth/handler/OAuth2AuthenticationSuccessHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.olive.pribee.module.auth.handler; - -import java.io.IOException; - -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; -import org.springframework.stereotype.Component; - -import com.olive.pribee.global.enums.JwtVo; -import com.olive.pribee.global.util.RedisUtil; -import com.olive.pribee.global.util.ResponseUtil; -import com.olive.pribee.module.auth.JwtTokenProvider; -import com.olive.pribee.module.auth.domain.entity.CustomOAuth2User; -import com.olive.pribee.module.auth.domain.entity.Member; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - private final JwtTokenProvider jwtTokenProvider; - private final RedisUtil redisUtil; - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws IOException { - CustomOAuth2User oAuth2User = (CustomOAuth2User)authentication.getPrincipal(); - Member member = oAuth2User.member(); - - // JWT 생성 - JwtVo jwtVo = jwtTokenProvider.generateTokens(member); - - // Redis에 토큰 저장 - redisUtil.setOpsForValue(member.getId() + "_refresh", jwtVo.getRefreshToken(), - jwtTokenProvider.getREFRESH_TOKEN_EXPIRATION()); - - // 클라이언트에 응답 - ResponseUtil.setDataResponse(response, HttpServletResponse.SC_OK, jwtVo); - - clearAuthenticationAttributes(request); - } -} diff --git a/src/main/java/com/olive/pribee/module/auth/service/CustomOAuth2UserService.java b/src/main/java/com/olive/pribee/module/auth/service/CustomOAuth2UserService.java deleted file mode 100644 index 580bcc1..0000000 --- a/src/main/java/com/olive/pribee/module/auth/service/CustomOAuth2UserService.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.olive.pribee.module.auth.service; - -import java.util.Map; - -import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Service; - -import com.olive.pribee.module.auth.domain.entity.CustomOAuth2User; -import com.olive.pribee.module.auth.domain.entity.Member; -import com.olive.pribee.module.auth.domain.repository.MemberRepository; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Service -@RequiredArgsConstructor -public class CustomOAuth2UserService extends DefaultOAuth2UserService { - private final MemberRepository memberRepository; - - @Override - public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { - OAuth2User oAuth2User = super.loadUser(userRequest); - String accessToken = userRequest.getAccessToken().getTokenValue(); - - // 받아올 정보 정의 - String facebookId = oAuth2User.getAttribute("id"); - String facebookName = oAuth2User.getAttribute("name"); - String facebookEmail = oAuth2User.getAttribute("email"); - String profilePictureUrl = getProfilePictureUrl(oAuth2User.getAttributes()); - - // 멤버 없다면 회원가입 - Member member = memberRepository.findByFacebookId(facebookId) - .orElseGet(() -> createNewMember(facebookId, facebookName, facebookEmail, profilePictureUrl)); - - return new CustomOAuth2User(member, oAuth2User.getAttributes()); - } - - // 프로필 사진 URL을 가져오는 헬퍼 메소드 - private String getProfilePictureUrl(Map attributes) { - Map pictureData = (Map)attributes.get("picture"); - return pictureData != null ? (String)((Map)pictureData.get("data")).get("url") : null; - } - - // 새로운 회원을 생성하는 메소드 - private Member createNewMember(String facebookId, String name, String email, String profilePictureUrl) { - Member newMember = Member.of(facebookId, name, email, profilePictureUrl); - return memberRepository.save(newMember); - } -} From 65f3f75d9fbfc9629a6b752835ad782b45c86ca7 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:05:33 +0900 Subject: [PATCH 05/19] =?UTF-8?q?fix:=20oauth=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20cors=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pribee/global/config/SecurityConfig.java | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/olive/pribee/global/config/SecurityConfig.java b/src/main/java/com/olive/pribee/global/config/SecurityConfig.java index b0e2865..ad7a441 100644 --- a/src/main/java/com/olive/pribee/global/config/SecurityConfig.java +++ b/src/main/java/com/olive/pribee/global/config/SecurityConfig.java @@ -7,6 +7,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -15,9 +16,6 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import com.olive.pribee.global.common.filter.JwtAuthenticationFilter; -import com.olive.pribee.module.auth.handler.OAuth2AuthenticationFailureHandler; -import com.olive.pribee.module.auth.handler.OAuth2AuthenticationSuccessHandler; -import com.olive.pribee.module.auth.service.CustomOAuth2UserService; import lombok.RequiredArgsConstructor; @@ -26,36 +24,34 @@ @RequiredArgsConstructor public class SecurityConfig { - @Value("${front.url}") + @Value("${url.test}") + private String TEST_URL; + + @Value("${url.front}") private String FRONT_URL; + @Value("${url.domain}") + private String DOMAIN_URL; + private final JwtAuthenticationFilter jwtAuthenticationFilter; - private final CustomOAuth2UserService customOAuth2UserService; - private final OAuth2AuthenticationSuccessHandler successHandler; - private final OAuth2AuthenticationFailureHandler failureHandler; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .csrf(csrf -> csrf.disable()) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .csrf(AbstractHttpConfigurer::disable) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers( "/api/auth/token", - "/oauth2/**", + "/api/auth/login/facebook", "/swagger-ui/**", "/webjars/**", "/swagger-ui.html", - "/v3/api-docs/**").permitAll() + "/v3/api-docs/**" + ).permitAll() .anyRequest().authenticated() ) - - // OAuth2 로그인 설정 - .oauth2Login(oauth2 -> oauth2 - .userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService)) - .successHandler(successHandler) - .failureHandler(failureHandler) - ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); @@ -65,15 +61,14 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti public CorsConfigurationSource corsConfigurationSource() { // 허용할 출처, HTTP 메서드, 헤더 설정 및 자격 증명 포함 설정 CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(List.of(FRONT_URL)); + configuration.setAllowedOrigins(List.of(TEST_URL, FRONT_URL, DOMAIN_URL)); configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); - configuration.setAllowedHeaders(List.of("Authorization", "Content-Type")); + configuration.setAllowedHeaders(List.of("*")); configuration.setAllowCredentials(true); - // 특정 API 경로에 대해 CORS 정책을 적용 + // 특정 API 경로에 대해 CORS 정책 제외 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/api/**", configuration); - source.registerCorsConfiguration("/oauth2/**", configuration); source.registerCorsConfiguration("/swagger-ui/**", configuration); source.registerCorsConfiguration("/v3/api-docs/**", configuration); source.registerCorsConfiguration("/webjars/**", configuration); From 4b493c84d22d86dc369d05a2e4e82fd246a27537 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:06:22 +0900 Subject: [PATCH 06/19] =?UTF-8?q?refactor:=20jwt=20=ED=82=A4=EC=9B=8C?= =?UTF-8?q?=EB=93=9C=20final=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20[?= =?UTF-8?q?PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/olive/pribee/global/config/OpenApiConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/olive/pribee/global/config/OpenApiConfig.java b/src/main/java/com/olive/pribee/global/config/OpenApiConfig.java index 84cdf55..bfb57fe 100644 --- a/src/main/java/com/olive/pribee/global/config/OpenApiConfig.java +++ b/src/main/java/com/olive/pribee/global/config/OpenApiConfig.java @@ -14,7 +14,7 @@ public class OpenApiConfig { private static final String BEARER_TOKEN_PREFIX = "bearer"; - private static String securityJwtName = "JWT"; + private static final String securityJwtName = "JWT"; @Bean public OpenAPI customOpenAPI() { From 907875a858314ca3a8b406fad7e88aa81d578730 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:11:25 +0900 Subject: [PATCH 07/19] =?UTF-8?q?fix:=20email=20null=20=EA=B0=92=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EC=83=81=ED=99=A9=20?= =?UTF-8?q?=EA=B3=A0=EB=A0=A4=ED=95=98=EC=97=AC=20null=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/olive/pribee/module/auth/domain/entity/Member.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/olive/pribee/module/auth/domain/entity/Member.java b/src/main/java/com/olive/pribee/module/auth/domain/entity/Member.java index 0c210c9..933bc54 100644 --- a/src/main/java/com/olive/pribee/module/auth/domain/entity/Member.java +++ b/src/main/java/com/olive/pribee/module/auth/domain/entity/Member.java @@ -41,7 +41,6 @@ public class Member extends BaseTime { @NotNull private String name; - @NotNull private String email; @NotNull @@ -51,7 +50,7 @@ public class Member extends BaseTime { @Enumerated(EnumType.STRING) private MemberRole role; - public static Member of(@NotNull String facebookId,@NotNull String name, @NotNull String email, @NotNull String profilePictureUrl) { + public static Member of(@NotNull String facebookId,@NotNull String name, String email, @NotNull String profilePictureUrl) { return Member.builder() .facebookId(facebookId) .name(name) From f48d8bb7dbac3b96b210f9aa94d72ff3804f10ef Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:12:02 +0900 Subject: [PATCH 08/19] =?UTF-8?q?chore:=20web=20client=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index e06efc0..2666f39 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' + // Web Client + implementation 'org.springframework.boot:spring-boot-starter-webflux' + // Spring Security & OAuth2 implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' From 63789a622a59047f46fa73a53900451fbf906fc6 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:13:20 +0900 Subject: [PATCH 09/19] =?UTF-8?q?feat:=20web=20client=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pribee/global/config/WebClientConfig.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/com/olive/pribee/global/config/WebClientConfig.java diff --git a/src/main/java/com/olive/pribee/global/config/WebClientConfig.java b/src/main/java/com/olive/pribee/global/config/WebClientConfig.java new file mode 100644 index 0000000..a340391 --- /dev/null +++ b/src/main/java/com/olive/pribee/global/config/WebClientConfig.java @@ -0,0 +1,17 @@ +package com.olive.pribee.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class WebClientConfig { + + @Bean + public WebClient.Builder webClientBuilder() { + return WebClient.builder() + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + } +} From 77b9e717b194e49fc819f34d08c6cd096e8954b2 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:15:17 +0900 Subject: [PATCH 10/19] =?UTF-8?q?feat:=20facebook=20accessToken=20api=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20dto=20=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/auth/dto/res/FacebookTokenRes.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/com/olive/pribee/module/auth/dto/res/FacebookTokenRes.java diff --git a/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookTokenRes.java b/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookTokenRes.java new file mode 100644 index 0000000..9436447 --- /dev/null +++ b/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookTokenRes.java @@ -0,0 +1,27 @@ +package com.olive.pribee.module.auth.dto.res; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class FacebookTokenRes { + private String accessToken; + private String tokenType; + private long expiresIn; + + @Builder + public FacebookTokenRes( + @JsonProperty("access_token") String accessToken, + @JsonProperty("token_type") String tokenType, + @JsonProperty("expires_in") long expiresIn + ) { + this.accessToken = accessToken; + this.tokenType = tokenType; + this.expiresIn = expiresIn; + } +} \ No newline at end of file From 5044685d53bbc3ef89a36f665f49be96f9d3cb89 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:15:34 +0900 Subject: [PATCH 11/19] =?UTF-8?q?feat:=20facebook=20user=20info=20api=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20dto=20=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/res/FacebookUserInfoPictureRes.java | 42 +++++++++++++++++++ .../auth/dto/res/FacebookUserInfoRes.java | 31 ++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/main/java/com/olive/pribee/module/auth/dto/res/FacebookUserInfoPictureRes.java create mode 100644 src/main/java/com/olive/pribee/module/auth/dto/res/FacebookUserInfoRes.java diff --git a/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookUserInfoPictureRes.java b/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookUserInfoPictureRes.java new file mode 100644 index 0000000..a230398 --- /dev/null +++ b/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookUserInfoPictureRes.java @@ -0,0 +1,42 @@ +package com.olive.pribee.module.auth.dto.res; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class FacebookUserInfoPictureRes { + + private Data data; + + @Builder + public FacebookUserInfoPictureRes(@JsonProperty("data") Data data) { + this.data = data; + } + + @Getter + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class Data { + private int height; + private boolean isSilhouette; + private String url; + private int width; + + @Builder + public Data( + @JsonProperty("height") int height, + @JsonProperty("is_silhouette") boolean isSilhouette, + @JsonProperty("url") String url, + @JsonProperty("width") int width + ) { + this.height = height; + this.isSilhouette = isSilhouette; + this.url = url; + this.width = width; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookUserInfoRes.java b/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookUserInfoRes.java new file mode 100644 index 0000000..ab756a8 --- /dev/null +++ b/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookUserInfoRes.java @@ -0,0 +1,31 @@ +package com.olive.pribee.module.auth.dto.res; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class FacebookUserInfoRes { + private String id; + private String name; + private String email; + private FacebookUserInfoPictureRes picture; + + @Builder + public FacebookUserInfoRes( + @JsonProperty("id") String id, + @JsonProperty("name") String name, + @JsonProperty("email") String email, + @JsonProperty("picture") FacebookUserInfoPictureRes picture + ) { + this.id = id; + this.name = name; + this.email = email; + this.picture = picture; + } + +} From eb0cc758b025f1b88920b691e032f90a4feca56d Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:17:30 +0900 Subject: [PATCH 12/19] =?UTF-8?q?feat:=20facebook=20api=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EC=9D=84=20=ED=86=B5=ED=95=B4=20=EB=B0=9B=EC=9D=84=20?= =?UTF-8?q?=EC=B5=9C=EC=A2=85=20=EC=9D=91=EB=8B=B5=20dto=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/auth/dto/res/FacebookAuthRes.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/com/olive/pribee/module/auth/dto/res/FacebookAuthRes.java diff --git a/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookAuthRes.java b/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookAuthRes.java new file mode 100644 index 0000000..5ed285c --- /dev/null +++ b/src/main/java/com/olive/pribee/module/auth/dto/res/FacebookAuthRes.java @@ -0,0 +1,26 @@ +package com.olive.pribee.module.auth.dto.res; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class FacebookAuthRes { + + private String facebookId; + private String longTermToken; + + @Builder + public FacebookAuthRes( + @JsonProperty("facebookId") String facebookId, + @JsonProperty("longTermToken") String longTermToken + + ) { + this.facebookId = facebookId; + this.longTermToken = longTermToken; + } +} \ No newline at end of file From c374b9cc3cd23f2c6f8edeee6f813636ee3e95e5 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:35:37 +0900 Subject: [PATCH 13/19] =?UTF-8?q?feat:=20facebook=20api(accessToken,=20lon?= =?UTF-8?q?g-term=20accessToken,=20userInfo)=20Weclient=20=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=9C=20=EC=9A=94=EC=B2=AD=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/FacebookAuthService.java | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/main/java/com/olive/pribee/module/auth/service/FacebookAuthService.java diff --git a/src/main/java/com/olive/pribee/module/auth/service/FacebookAuthService.java b/src/main/java/com/olive/pribee/module/auth/service/FacebookAuthService.java new file mode 100644 index 0000000..b83aa5b --- /dev/null +++ b/src/main/java/com/olive/pribee/module/auth/service/FacebookAuthService.java @@ -0,0 +1,131 @@ +package com.olive.pribee.module.auth.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.reactive.function.client.WebClient; + +import com.fasterxml.jackson.databind.JsonNode; +import com.olive.pribee.global.error.GlobalErrorCode; +import com.olive.pribee.global.error.exception.AppException; +import com.olive.pribee.module.auth.dto.res.FacebookAuthRes; +import com.olive.pribee.module.auth.dto.res.FacebookTokenRes; +import com.olive.pribee.module.auth.dto.res.FacebookUserInfoRes; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +@Slf4j +public class FacebookAuthService { + + private final String FB_EXCHANGE_TOKEN = "fb_exchange_token"; + + @Value("${url.facebook}") + private String FACEBOOK_BASE_URL; + + @Value("${facebook.clientId}") + private String FACEBOOK_CLIENT_ID; + + @Value("${facebook.clientSecret}") + private String FACEBOOK_CLIENT_SECRET; + + @Value("${facebook.redirect-uri}") + private String FACEBOOK_REDIRECT_URI; + + private final WebClient.Builder webClientBuilder; + + // Facebook WebClient 생성 + private WebClient getFacebookWebClient() { + return webClientBuilder.baseUrl(FACEBOOK_BASE_URL).build(); + } + + public Mono getFacebookIdWithToken(String code) { + return exchangeCodeForAccessToken(code) // (1) + .flatMap(shortLivedToken -> + exchangeForLongTermAccessToken(shortLivedToken) // (2) + .flatMap(longTermToken -> + fetchFacebookId(longTermToken) // (3) + .map(facebookId -> new FacebookAuthRes(facebookId, longTermToken)) + ) + ) + .onErrorResume(Exception.class, ex -> { + log.error("[Facebook] Facebook ID & Long Term Token 가져오기 실패: {}", ex.getMessage(), ex); + return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR)); + }); + } + + // (1) Facebook Authorization Code → Short-Lived Access Token 변환 + private Mono exchangeCodeForAccessToken(String code) { + return getFacebookWebClient().get() + .uri(uriBuilder -> uriBuilder + .path("/oauth/access_token") + .queryParam("client_id", FACEBOOK_CLIENT_ID) + .queryParam("client_secret", FACEBOOK_CLIENT_SECRET) + .queryParam("redirect_uri", FACEBOOK_REDIRECT_URI) + .queryParam("code", code) + .build()) + .retrieve() + .bodyToMono(FacebookTokenRes.class) + .map(FacebookTokenRes::getAccessToken) // Access Token 추출 + .onErrorResume(Exception.class, ex -> { + log.error("[Facebook] AccessToken 요청 실패: {}", ex.getMessage(), ex); + return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR)); + }); + } + + // (2) Short-Lived Token → Long-Term Token 변환 + private Mono exchangeForLongTermAccessToken(String shortLivedToken) { + return getFacebookWebClient().get() + .uri(uriBuilder -> uriBuilder + .path("/oauth/access_token") + .queryParam("grant_type", FB_EXCHANGE_TOKEN) + .queryParam("client_id", FACEBOOK_CLIENT_ID) + .queryParam("client_secret", FACEBOOK_CLIENT_SECRET) + .queryParam("fb_exchange_token", shortLivedToken) + .build()) + .retrieve() + .bodyToMono(FacebookTokenRes.class) + .map(FacebookTokenRes::getAccessToken) + .onErrorResume(Exception.class, ex -> { + log.error("[Facebook] Long Term AccessToken 요청 실패: {}", ex.getMessage(), ex); + return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR)); + }); + } + + // (3) Long-Term Token으로 Facebook ID 조회 + private Mono fetchFacebookId(String accessToken) { + return getFacebookWebClient().get() + .uri(uriBuilder -> uriBuilder + .path("/me") + .queryParam("fields", "id") + .queryParam("access_token", accessToken) + .build()) + .retrieve() + .bodyToMono(JsonNode.class) + .map(json -> json.get("id").asText()) // JSON에서 id 값 추출 + .onErrorResume(Exception.class, ex -> { + log.error("[Facebook] Facebook ID 조회 실패: {}", ex.getMessage(), ex); + return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR)); + }); + } + + // (4-1) Facebook 사용자 정보 조회 + public Mono fetchFacebookUserInfo(String accessToken) { + return getFacebookWebClient().get() + .uri(uriBuilder -> uriBuilder + .path("/me") + .queryParam("fields", "id,name,email,picture.width(1000).height(1000)") + .queryParam("access_token", accessToken) + .build()) + .retrieve() + .bodyToMono(FacebookUserInfoRes.class) + .onErrorResume(Exception.class, ex -> { + log.error("[Facebook] Facebook 사용자 정보 조회 실패: {}", ex.getMessage(), ex); + return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR)); + }); + } +} From 0606e6e383129a86e3e9ae4e648773f0fbe266e7 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 15:36:10 +0900 Subject: [PATCH 14/19] =?UTF-8?q?feat:=20facebook=20code=20=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20jwt=20=EB=B0=9C=EA=B8=89=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/auth/service/MemberService.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/olive/pribee/module/auth/service/MemberService.java b/src/main/java/com/olive/pribee/module/auth/service/MemberService.java index 77fd27f..bd624f3 100644 --- a/src/main/java/com/olive/pribee/module/auth/service/MemberService.java +++ b/src/main/java/com/olive/pribee/module/auth/service/MemberService.java @@ -11,6 +11,8 @@ import com.olive.pribee.module.auth.JwtTokenProvider; import com.olive.pribee.module.auth.domain.entity.Member; import com.olive.pribee.module.auth.domain.repository.MemberRepository; +import com.olive.pribee.module.auth.dto.res.FacebookAuthRes; +import com.olive.pribee.module.auth.dto.res.FacebookUserInfoRes; import com.olive.pribee.module.auth.dto.res.LoginResDto; import io.jsonwebtoken.ExpiredJwtException; @@ -23,13 +25,55 @@ @RequiredArgsConstructor @Slf4j public class MemberService { + private final FacebookAuthService facebookAuthService; private final JwtTokenProvider jwtTokenProvider; private final RedisUtil redisUtil; private final MemberRepository memberRepository; + // facebook code 기반 facebook 로그인을 통한 접근 jwt 발급 + @Transactional + public LoginResDto getAccessToken(String code) { + // code 기반 facebook ID 조회 + FacebookAuthRes facebookAuthRes = facebookAuthService.getFacebookIdWithToken(code).block(); + if (facebookAuthRes == null) { + log.error("[Facebook] facebookAuthRes is null in memberService -- " + code); + throw new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR); + } + + // facebook ID 기반 DB에서 회원 조회 + Member member = memberRepository.findByFacebookId(facebookAuthRes.getFacebookId()) + .orElseGet(() -> { + // 저장된 회원이 없으면 Facebook API에서 회원 정보 조회 + FacebookUserInfoRes userInfo = facebookAuthService.fetchFacebookUserInfo( + facebookAuthRes.getLongTermToken()).block(); + if (userInfo == null) { + log.error( + "[Facebook] facebook userInfo is null in memberService -- " + facebookAuthRes.getFacebookId()); + throw new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR); + } + + return memberRepository.save(Member.of( + userInfo.getId(), + userInfo.getName(), + userInfo.getEmail(), + userInfo.getPicture().getData().getUrl() + )); + }); + + // facebook long live accessToken Redis 에 저장 + redisUtil.setOpsForValue(member.getId() + "_fb_access", facebookAuthRes.getLongTermToken(), 5184000); + + // JWT 토큰 생성 및 refreshToken 저장 + JwtVo jwtVo = jwtTokenProvider.generateTokens(member); + redisUtil.setOpsForValue(member.getId() + "_refresh", jwtVo.getRefreshToken(), + jwtTokenProvider.getREFRESH_TOKEN_EXPIRATION()); + + return LoginResDto.of(jwtVo.getAccessToken(), jwtVo.getRefreshToken()); + } + // refresh token 으로 새로운 accessToken 발급 @Transactional - public LoginResDto getAccessToken(String refreshToken) { + public LoginResDto getNewAccessToken(String refreshToken) { if (refreshToken.isBlank()) { throw new AppException(GlobalErrorCode.REFRESH_TOKEN_REQUIRED); } From 26ebb8454d194f095ac8f3a1e963aa716991311d Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 16:01:17 +0900 Subject: [PATCH 15/19] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20jwt=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EC=95=88=ED=95=98=EB=8A=94=20url=20=EC=88=98=EC=A0=95=20[PRBE-?= =?UTF-8?q?15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pribee/global/common/filter/JwtAuthenticationFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/olive/pribee/global/common/filter/JwtAuthenticationFilter.java b/src/main/java/com/olive/pribee/global/common/filter/JwtAuthenticationFilter.java index b9c0a9a..bc9446c 100644 --- a/src/main/java/com/olive/pribee/global/common/filter/JwtAuthenticationFilter.java +++ b/src/main/java/com/olive/pribee/global/common/filter/JwtAuthenticationFilter.java @@ -40,7 +40,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // accessToken 이 필요없는 경우 필터링 없이 처리 if (requestURI.startsWith("/api/auth/token") || - requestURI.startsWith("/oauth2")) { // oauth2/authorization/facebook + requestURI.startsWith("/api/auth/login/facebook")) { chain.doFilter(request, response); return; } From 4017bce43778c919e5bb2252c6c868a2e3609fd2 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 16:01:36 +0900 Subject: [PATCH 16/19] =?UTF-8?q?feat:=20facebook=20code=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EC=97=90=EB=9F=AC=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/olive/pribee/global/error/GlobalErrorCode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/olive/pribee/global/error/GlobalErrorCode.java b/src/main/java/com/olive/pribee/global/error/GlobalErrorCode.java index ac4d450..006d876 100644 --- a/src/main/java/com/olive/pribee/global/error/GlobalErrorCode.java +++ b/src/main/java/com/olive/pribee/global/error/GlobalErrorCode.java @@ -6,6 +6,7 @@ @Getter public enum GlobalErrorCode implements ErrorCode { + INVALID_FACEBOOK_CODE(HttpStatus.UNAUTHORIZED, "facebook code가 유효하지 않습니다."), AUTHENTICATION_FAILED(HttpStatus.UNAUTHORIZED, "인증에 실패하였습니다."), AUTHORIZATION_FAILED(HttpStatus.UNAUTHORIZED, "인가에 실패하였습니다."), ACCESS_TOKEN_REQUIRED(HttpStatus.UNAUTHORIZED, "Access Token이 필요합니다."), From 325179e052090c437f7a957d010d0160d3c5a65c Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 16:02:04 +0900 Subject: [PATCH 17/19] =?UTF-8?q?feat:=20facebook=20code=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/auth/service/FacebookAuthService.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/com/olive/pribee/module/auth/service/FacebookAuthService.java b/src/main/java/com/olive/pribee/module/auth/service/FacebookAuthService.java index b83aa5b..ce5678b 100644 --- a/src/main/java/com/olive/pribee/module/auth/service/FacebookAuthService.java +++ b/src/main/java/com/olive/pribee/module/auth/service/FacebookAuthService.java @@ -1,6 +1,7 @@ package com.olive.pribee.module.auth.service; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.reactive.function.client.WebClient; @@ -53,6 +54,10 @@ public Mono getFacebookIdWithToken(String code) { ) ) .onErrorResume(Exception.class, ex -> { + if (ex instanceof AppException) { + return Mono.error(ex); + } + log.error("[Facebook] Facebook ID & Long Term Token 가져오기 실패: {}", ex.getMessage(), ex); return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR)); }); @@ -69,9 +74,18 @@ private Mono exchangeCodeForAccessToken(String code) { .queryParam("code", code) .build()) .retrieve() + .onStatus(status -> + status == HttpStatus.UNAUTHORIZED || status == HttpStatus.BAD_REQUEST, response ->{ + log.error("[Facebook] Invalid Facebook Code: {}", code); + return Mono.error(new AppException(GlobalErrorCode.INVALID_FACEBOOK_CODE)); + }) .bodyToMono(FacebookTokenRes.class) .map(FacebookTokenRes::getAccessToken) // Access Token 추출 .onErrorResume(Exception.class, ex -> { + if (ex instanceof AppException) { + return Mono.error(ex); + } + log.error("[Facebook] AccessToken 요청 실패: {}", ex.getMessage(), ex); return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR)); }); From 590c7e7212923768b9acc45a13c1c8581aa1e7cb Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 16:03:07 +0900 Subject: [PATCH 18/19] =?UTF-8?q?feat:=20facebook=20code=20=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20jwt=20=EB=B0=9C=EA=B8=89=20api=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pribee/module/auth/controller/MemberController.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/olive/pribee/module/auth/controller/MemberController.java b/src/main/java/com/olive/pribee/module/auth/controller/MemberController.java index 8da13af..817809f 100644 --- a/src/main/java/com/olive/pribee/module/auth/controller/MemberController.java +++ b/src/main/java/com/olive/pribee/module/auth/controller/MemberController.java @@ -24,9 +24,15 @@ public class MemberController implements MemberControllerDocs { private final MemberService memberService; + @GetMapping("/login/facebook") + public ResponseEntity getLogin(@RequestHeader("facebook-code") String code){ + LoginResDto resDto = memberService.getAccessToken(code); + return ResponseEntity.status(201).body(DataResponseDto.of(resDto, 201)); + } + @GetMapping("/token") public ResponseEntity getAccessToken(@RequestHeader("Authorization-Refresh") String refreshToken) { - LoginResDto resDto = memberService.getAccessToken(refreshToken); + LoginResDto resDto = memberService.getNewAccessToken(refreshToken); return ResponseEntity.status(201).body(DataResponseDto.of(resDto, 201)); } From fbd0f3ed0636357b8de4263ced1ec416f15173e7 Mon Sep 17 00:00:00 2001 From: yerim1ee Date: Wed, 5 Mar 2025 16:03:25 +0900 Subject: [PATCH 19/19] =?UTF-8?q?docs:=20facebook=20code=20=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20jwt=20=EB=B0=9C=EA=B8=89=20api=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=ED=99=94=20=EC=B6=94=EA=B0=80=20[PRBE-15]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/MemberControllerDocs.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/olive/pribee/module/auth/controller/MemberControllerDocs.java b/src/main/java/com/olive/pribee/module/auth/controller/MemberControllerDocs.java index b8ef3a6..8e65591 100644 --- a/src/main/java/com/olive/pribee/module/auth/controller/MemberControllerDocs.java +++ b/src/main/java/com/olive/pribee/module/auth/controller/MemberControllerDocs.java @@ -16,7 +16,29 @@ @Tag(name = "Member", description = "사용자 관련 API") public interface MemberControllerDocs { - @Operation(summary = "accessToken 발급", description = "리프레시 토큰을 이용해 엑세스 토큰을 발급합니다.") + @Operation(summary = "로그인", description = "facebook code 를 통해 로그인을 발급합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "Created", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ResponseDto.class), + examples = + @ExampleObject(value = "{ \"code\": 201, \"message\": \"Created\" }") + ) + ), + @ApiResponse(responseCode = "401", description = "인증에 실패하였습니다.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ResponseDto.class), + examples = + @ExampleObject(value = "{ \"code\": 401, \"message\": \"facebook code가 유효하지 않습니다.\" }") + + ) + ) + }) + ResponseEntity getLogin(String code); + + @Operation(summary = "accessToken 재발급", description = "리프레시 토큰을 이용해 엑세스 토큰을 발급합니다.") @ApiResponses({ @ApiResponse(responseCode = "201", description = "Created", content = @Content(