From 42df1e493b051bc57dbd684a7c427a6cdf5a9bbf Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Thu, 10 Oct 2024 18:31:35 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=EA=B0=9C=EB=B0=9C=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../devlinkbackend/config/CorsConfig.java | 38 ++++++++++ .../config/JwtAuthenticationFilter.java | 14 ++++ .../devlinkbackend/config/SecurityConfig.java | 75 +++++++++---------- .../devlinkbackend/config/SwaggerConfig.java | 18 +++++ .../mtvs/devlinkbackend/config/WebConfig.java | 15 ++++ src/main/resources/application.yml | 20 +++++ 7 files changed, 144 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/config/SwaggerConfig.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/config/WebConfig.java diff --git a/build.gradle b/build.gradle index ee2963c..74d5124 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,8 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' // 또는 jjwt-gson 사용 시 'io.jsonwebtoken:jjwt-gson:0.11.2' + implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' // 최신 버전으로 변경 가능 + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.0' // 최신 버전 확인 compileOnly 'org.projectlombok:lombok:1.18.30' // 최신 버전 확인 후 사용 annotationProcessor 'org.projectlombok:lombok:1.18.30' developmentOnly 'org.springframework.boot:spring-boot-devtools' diff --git a/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java new file mode 100644 index 0000000..47448ff --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java @@ -0,0 +1,38 @@ +package com.mtvs.devlinkbackend.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@Configuration +public class CorsConfig { + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + + configuration.addAllowedOrigin("http://localhost:8080"); + configuration.addAllowedOrigin("http://localhost:5173"); // 테스트에서 사용되는 도메인 추가 + + configuration.addAllowedMethod("GET"); + configuration.addAllowedMethod("POST"); + configuration.addAllowedMethod("PUT"); + configuration.addAllowedMethod("PATCH"); + configuration.addAllowedMethod("DELETE"); + + configuration.addAllowedHeader("*"); // 모든 헤더 허용 + + configuration.setAllowCredentials(true); // 인증 정보 포함 허용 + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + + @Bean + public CorsFilter corsFilter(CorsConfigurationSource corsConfigurationSource) { + return new CorsFilter(corsConfigurationSource); + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java index a248ef6..80ba081 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java @@ -28,6 +28,8 @@ public JwtAuthenticationFilter(JwtUtil jwtUtil, EpicGamesTokenService epicGamesT protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + System.out.println("JwtAuthenticationFilter 실행됨: " + request.getRequestURI()); + // Authorization 헤더에서 Bearer 토큰 추출 String authorizationHeader = request.getHeader("Authorization"); String token = null; @@ -68,6 +70,18 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse chain.doFilter(request, response); } + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + String path = request.getRequestURI(); + // Swagger 관련 모든 경로 예외 처리 + return path.startsWith("/swagger-ui") + || path.startsWith("/v3/api-docs") + || path.equals("/swagger-ui.html") + || path.startsWith("/swagger-resources") + || path.startsWith("/webjars") + || path.startsWith("/login"); + } + // 쿠키에서 리프레시 토큰을 추출하는 메서드 private String getRefreshTokenFromCookies(HttpServletRequest request) { if (request.getCookies() != null) { diff --git a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java index a5c3aaa..be7fa56 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java @@ -9,6 +9,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.core.Authentication; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; @@ -21,10 +22,8 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; +import org.springframework.security.web.session.InvalidSessionStrategy; +import org.springframework.web.cors.CorsConfigurationSource; @Configuration @EnableWebSecurity @@ -32,10 +31,12 @@ public class SecurityConfig { private final UserService userService; private final OAuth2AuthorizedClientService authorizedClientService; + private final CorsConfigurationSource corsConfigurationSource; private final JwtAuthenticationFilter jwtAuthenticationFilter; - public SecurityConfig(UserService userService, OAuth2AuthorizedClientService authorizedClientService, JwtAuthenticationFilter jwtAuthenticationFilter) { + public SecurityConfig(UserService userService, CorsConfigurationSource corsConfigurationSource, OAuth2AuthorizedClientService authorizedClientService, JwtAuthenticationFilter jwtAuthenticationFilter) { this.userService = userService; + this.corsConfigurationSource = corsConfigurationSource; this.authorizedClientService = authorizedClientService; this.jwtAuthenticationFilter = jwtAuthenticationFilter; } @@ -43,31 +44,53 @@ public SecurityConfig(UserService userService, OAuth2AuthorizedClientService aut @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http + .cors(cors -> cors.configurationSource(corsConfigurationSource)) // CORS 설정 + .csrf(AbstractHttpConfigurer::disable) // CSRF 비활성화 + .sessionManagement(sessionManagement -> sessionManagement + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) .authorizeHttpRequests(authorizeRequests -> authorizeRequests - .requestMatchers("*", "/question/**", "/login").permitAll() // "*"에 대한 설정은 CorsTest를 위함 + .requestMatchers( + "/", + "/login", + "/v3/api-docs/**", // Swagger API Docs 경로 + "/swagger-ui/**", // Swagger UI 정적 리소스 경로 + "/swagger-ui.html", // Swagger UI 페이지 경로 + "/swagger-resources/**", // Swagger 관련 리소스 경로 + "/webjars/**", // Webjars로 제공되는 Swagger 리소스 경로 + "/configuration/ui", // 추가 Swagger 설정 경로 + "/configuration/security", // 추가 Swagger 설정 경로 + "/error" // 오류 페이지 경로 허용 + ).permitAll() // Swagger 관련 경로 허용 .anyRequest().authenticated() ) + // oauth2Login 설정이 다른 경로에서만 작동하도록 설정 .oauth2Login(oauth2Login -> oauth2Login .loginPage("/login") .defaultSuccessUrl("/", true) + .failureUrl("/login?error=true") .userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint .userService(oauth2UserService()) ) - .successHandler(oauth2AuthenticationSuccessHandler()) // 성공 핸들러 추가 + .successHandler(oauth2AuthenticationSuccessHandler()) ) .logout(logout -> logout .logoutUrl("/logout") .addLogoutHandler(logoutHandler()) - .logoutSuccessHandler(logoutSuccessHandler()) + .logoutSuccessUrl("/") ) - // 세션을 생성하지 않도록 설정 - .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - // JWT 필터 추가 - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) - .addFilter(corsFilter()); + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); +// +// return http.build(); } + // 가능성 : + // "/swagger" 경로로 들어온 요청은 필터에서 permitAll로 권한 인증을 통과했지만 + // "index.html"로 서블릿 포워딩 되면서 시큐리티 필터를 다시 거치게 되는데 권한 없음으로 403이 내려오는 것이다. + // 요청 한번에 권한 인증이 여러번 되는 이유는 직접 구현한 토큰 인증 필터와 같은 경우 OncePerRequestFilter를 확장해서 + // 요청 당 한번만 거치도록 설정하지만 기본적으로 필터는 서블릿에 대한 매요청마다 거치는 것이 기본 전략임 + @Bean public OAuth2UserService oauth2UserService() { return new DefaultOAuth2UserService(); @@ -76,6 +99,7 @@ public OAuth2UserService oauth2UserService() { @Bean public AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler() { return (HttpServletRequest request, HttpServletResponse response, Authentication authentication) -> { + System.out.println("여기 오냐"); OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal(); // OAuth2AuthorizedClient를 사용하여 액세스 토큰과 리프레시 토큰 가져오기 @@ -106,14 +130,6 @@ public LogoutHandler logoutHandler() { }; } - @Bean - public LogoutSuccessHandler logoutSuccessHandler() { - return (HttpServletRequest request, HttpServletResponse response, Authentication authentication) -> { - // 로그아웃 성공 후 리디렉트 - response.sendRedirect("/login?logout"); - }; - } - private void deleteCookie(HttpServletResponse response, String cookieName) { Cookie cookie = new Cookie(cookieName, null); cookie.setPath("/"); @@ -122,21 +138,4 @@ private void deleteCookie(HttpServletResponse response, String cookieName) { cookie.setSecure(true); response.addCookie(cookie); } - - @Bean - public CorsFilter corsFilter() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.addAllowedOrigin("http://localhost:5173"); // 테스트에서 사용되는 도메인 추가 - configuration.addAllowedMethod("GET"); - configuration.addAllowedMethod("POST"); - configuration.addAllowedMethod("PUT"); - configuration.addAllowedMethod("PATCH"); - configuration.addAllowedMethod("DELETE"); - configuration.addAllowedHeader("*"); // 모든 헤더 허용 - configuration.setAllowCredentials(true); // 인증 정보 포함 허용 - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); // 모든 경로에 대해 CORS 설정 적용 - return new CorsFilter(source); - } } \ No newline at end of file diff --git a/src/main/java/com/mtvs/devlinkbackend/config/SwaggerConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/SwaggerConfig.java new file mode 100644 index 0000000..646af86 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/config/SwaggerConfig.java @@ -0,0 +1,18 @@ +package com.mtvs.devlinkbackend.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("DevLink API Documentation") + .version("1.0.0") + .description("API documentation for DevLink Backend")); + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/config/WebConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/WebConfig.java new file mode 100644 index 0000000..efddc4b --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/config/WebConfig.java @@ -0,0 +1,15 @@ +package com.mtvs.devlinkbackend.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springdoc-openapi-ui/") + .setCachePeriod(3600); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2880310..37d28f2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,3 +32,23 @@ spring: properties: hibernate: format_sql: true +# web: +# cors: +# allow-credentials: true +# allowed-origin-patterns: +# - "http://localhost:8080" +# - "http://localhost:5173" +# allowed-methods: +# - GET +# - POST +# - PUT +# - PATCH +# - DELETE +# - OPTIONS +# allowed-headers: "*" +# +logging: + level: + org: + springframework: + security: DEBUG \ No newline at end of file From f09fd73a7c13f9cf8f08ede9649d719dca14b4b2 Mon Sep 17 00:00:00 2001 From: HEX <123macanic@naver.com> Date: Thu, 10 Oct 2024 22:52:41 +0900 Subject: [PATCH 2/6] =?UTF-8?q?Feat:=20Swagger=20Docs=20API=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 +++---- .../com/mtvs/devlinkbackend/config/SecurityConfig.java | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 74d5124..b72d4b2 100644 --- a/build.gradle +++ b/build.gradle @@ -22,14 +22,13 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'me.paulschwarz:spring-dotenv:4.0.0' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client:3.2.4' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' // 3.3.x 버전으로 변경 없음 implementation 'com.nimbusds:nimbus-jose-jwt:9.37.2' implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' // 또는 jjwt-gson 사용 시 'io.jsonwebtoken:jjwt-gson:0.11.2' - implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' // 최신 버전으로 변경 가능 - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.0' // 최신 버전 확인 - compileOnly 'org.projectlombok:lombok:1.18.30' // 최신 버전 확인 후 사용 + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' // Spring Boot 3.3.4에 맞는 2.x 버전 + compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.mysql:mysql-connector-j' diff --git a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java index be7fa56..60aa9ee 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java @@ -80,8 +80,6 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .logoutSuccessUrl("/") ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); -// -// return http.build(); } From 09d304a66d4d11e9b403db382b88e034cf3cefdf Mon Sep 17 00:00:00 2001 From: HEX <123macanic@naver.com> Date: Fri, 11 Oct 2024 00:04:34 +0900 Subject: [PATCH 3/6] =?UTF-8?q?Feat:=20=EB=AA=A8=EB=93=A0=20Controller?= =?UTF-8?q?=EC=97=90=20@Operation,=20@ApiResponse=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ether/controller/EtherController.java | 43 ++++++++++++-- .../controller/Oauth2UserController.java | 31 +++++++++- .../controller/QuestionController.java | 57 +++++++++++++++++++ .../reply/controller/ReplyController.java | 39 +++++++++++++ src/main/resources/application.yml | 26 ++++----- 5 files changed, 174 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/mtvs/devlinkbackend/ether/controller/EtherController.java b/src/main/java/com/mtvs/devlinkbackend/ether/controller/EtherController.java index 8eab829..3aec6a7 100644 --- a/src/main/java/com/mtvs/devlinkbackend/ether/controller/EtherController.java +++ b/src/main/java/com/mtvs/devlinkbackend/ether/controller/EtherController.java @@ -5,6 +5,9 @@ import com.mtvs.devlinkbackend.ether.dto.EtherUpdateRequestDTO; import com.mtvs.devlinkbackend.ether.entity.Ether; import com.mtvs.devlinkbackend.ether.service.EtherService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -21,7 +24,12 @@ public EtherController(EtherService etherService, JwtUtil jwtUtil) { this.jwtUtil = jwtUtil; } - // Create + @Operation(summary = "새 에테르 이력 등록", description = "새 에테르 이력를 등록합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "에테르가 성공적으로 등록됨"), + @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) @PostMapping public ResponseEntity registEther( @RequestBody EtherRegistRequestDTO etherRegistRequestDTO, @@ -32,14 +40,23 @@ public ResponseEntity registEther( return ResponseEntity.ok(newEther); } - // Read - Find by Ether ID + @Operation(summary = "Ether ID로 Ether 조회", description = "Ether ID를 사용하여 Ether를 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ether를 성공적으로 찾음"), + @ApiResponse(responseCode = "401", description = "인증되지 않음"), + @ApiResponse(responseCode = "404", description = "Ether를 찾을 수 없음") + }) @GetMapping("/{etherId}") public ResponseEntity findEtherByEtherId(@PathVariable Long etherId) { Ether ether = etherService.findEtherByEtherId(etherId); return ether != null ? ResponseEntity.ok(ether) : ResponseEntity.notFound().build(); } - // Read - Find by Account ID + @Operation(summary = "계정 ID로 Ether 목록 조회", description = "계정 ID를 사용하여 관련된 모든 Ether를 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ether 목록을 성공적으로 찾음"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) @GetMapping("/account") public ResponseEntity> findEthersByAccountId( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { @@ -49,14 +66,23 @@ public ResponseEntity> findEthersByAccountId( return ResponseEntity.ok(ethers); } - // Read - Find by Reason + @Operation(summary = "이유로 Ether 목록 조회", description = "지정된 이유로 관련된 모든 Ether를 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ether 목록을 성공적으로 찾음"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) @GetMapping("/reason/{reason}") public ResponseEntity> findEthersByReason(@PathVariable String reason) { List ethers = etherService.findEthersByReason(reason); return ResponseEntity.ok(ethers); } - // Update + @Operation(summary = "Ether 수정", description = "제공된 데이터를 기반으로 특정 Ether를 수정합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ether가 성공적으로 수정됨"), + @ApiResponse(responseCode = "400", description = "잘못된 수정 데이터"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) @PatchMapping public ResponseEntity updateEther(@RequestBody EtherUpdateRequestDTO etherUpdateRequestDTO) { try { @@ -67,7 +93,12 @@ public ResponseEntity updateEther(@RequestBody EtherUpdateRequestDTO ethe } } - // Delete + @Operation(summary = "Ether 삭제", description = "Ether ID를 사용하여 Ether를 삭제합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Ether가 성공적으로 삭제됨"), + @ApiResponse(responseCode = "401", description = "인증되지 않음"), + @ApiResponse(responseCode = "404", description = "Ether를 찾을 수 없음") + }) @DeleteMapping("/{etherId}") public ResponseEntity deleteEtherByEtherId(@PathVariable Long etherId) { etherService.deleteEtherByEtherId(etherId); diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java index a503a8b..25a333f 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java @@ -2,6 +2,9 @@ import com.mtvs.devlinkbackend.oauth2.service.EpicGamesTokenService; import com.mtvs.devlinkbackend.oauth2.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.*; @@ -23,6 +26,15 @@ public Oauth2UserController(EpicGamesTokenService epicGamesTokenService, UserSer // 로컬 user 정보 가져오는 API @GetMapping("/local/user-info") + @Operation( + summary = "로컬 유저 정보 조회", + description = "DevLink만의 DB에 저장된 유저 정보를 조회한다." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) public ResponseEntity getLocalUserInfo(@RequestHeader("Authorization") String authorizationHeader) { try { String token = extractToken(authorizationHeader); @@ -35,6 +47,15 @@ public ResponseEntity getLocalUserInfo(@RequestHeader("Authorization") String // epicgames 계정 정보 가져오는 API @GetMapping("/epicgames/user-info") + @Operation( + summary = "EpicGames 유저 정보 조회", + description = "EpicGames의 유저 정보를 조회한다." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) public ResponseEntity getEpicGamesUserInfo( @RequestHeader("Authorization") String authorizationHeader) { @@ -49,6 +70,14 @@ public ResponseEntity getEpicGamesUserInfo( } @PostMapping("/epicgames/callback") + @Operation( + summary = "EpicGames AccessToken 요청", + description = "EpicGames로부터 사용자에게 AccessToken을 전달한다." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "AccessToken 생성 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + }) public ResponseEntity handleEpicGamesCallback( @RequestBody Map payload, HttpServletResponse response) { @@ -71,7 +100,7 @@ public ResponseEntity handleEpicGamesCallback( response.addCookie(accessTokenCookie); response.addCookie(refreshTokenCookie); - return ResponseEntity.ok().build(); + return ResponseEntity.status(HttpStatus.CREATED).build(); } return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); diff --git a/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java b/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java index 7169b22..da7343c 100644 --- a/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java +++ b/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java @@ -5,6 +5,9 @@ import com.mtvs.devlinkbackend.question.dto.QuestionUpdateRequestDTO; import com.mtvs.devlinkbackend.question.entity.Question; import com.mtvs.devlinkbackend.question.service.QuestionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -26,6 +29,15 @@ public QuestionController(QuestionService questionService, JwtUtil jwtUtil) { // Create a new question @PostMapping + @Operation( + summary = "공개 질문 생성", + description = "사용자가 올린 공개 질문을 생성한다." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "질문 생성 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) public ResponseEntity createQuestion( @RequestBody QuestionRegistRequestDTO questionRegistRequestDTO, @RequestHeader(name = "Authorization") String accessToken) throws Exception { @@ -37,6 +49,15 @@ public ResponseEntity createQuestion( // Retrieve a question by ID @GetMapping("/{questionId}") + @Operation( + summary = "PK에 따른 질문 조회", + description = "PK값으로 사용자가 올린 공개 질문 1개를 조회한다." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) public ResponseEntity getQuestionById(@PathVariable Long questionId) { Question question = questionService.findQuestionByQuestionId(questionId); if (question != null) { @@ -48,6 +69,15 @@ public ResponseEntity getQuestionById(@PathVariable Long questionId) { // Retrieve all questions with pagination @GetMapping("/all") + @Operation( + summary = "Pagination으로 전체 질문 조회", + description = "Pagination으로 공개 질문 전체를 조회한다. 최대 20개가 주어진다" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) public ResponseEntity> getAllQuestionsWithPaging(@RequestParam int page) { List questions = questionService.findAllQuestionsWithPaging(page); return ResponseEntity.ok(questions); @@ -55,6 +85,15 @@ public ResponseEntity> getAllQuestionsWithPaging(@RequestParam in // Retrieve questions by account ID with pagination @GetMapping + @Operation( + summary = "Pagination으로 로그인한 사용자의 질문 조회", + description = "Pagination으로 사용자가 했던 질문 전체를 조회한다. 최대 20개가 주어진다" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) public ResponseEntity> getQuestionsByAccountIdWithPaging( @RequestParam int page, @RequestHeader(name = "Authorization") String accessToken) throws Exception { @@ -66,6 +105,15 @@ public ResponseEntity> getQuestionsByAccountIdWithPaging( // Update a question by ID @PatchMapping("/{id}") + @Operation( + summary = "사용자 질문 수정", + description = "사용자가 했던 질문을 수정한다." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수정 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) public ResponseEntity updateQuestion( @RequestBody QuestionUpdateRequestDTO questionUpdateRequestDTO, @RequestHeader(name = "Authorization") String accessToken) throws Exception { @@ -81,6 +129,15 @@ public ResponseEntity updateQuestion( // Delete a question by ID @DeleteMapping("/{questionId}") + @Operation( + summary = "사용자 질문 삭제", + description = "사용자가 했던 질문을 삭제한다" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수정 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) public ResponseEntity deleteQuestion(@PathVariable Long questionId) { questionService.deleteQuestion(questionId); diff --git a/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java b/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java index 1314eae..c26ed24 100644 --- a/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java +++ b/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java @@ -5,6 +5,9 @@ import com.mtvs.devlinkbackend.reply.dto.ReplyUpdateRequestDTO; import com.mtvs.devlinkbackend.reply.entity.Reply; import com.mtvs.devlinkbackend.reply.service.ReplyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -22,6 +25,12 @@ public ReplyController(ReplyService replyService, JwtUtil jwtUtil) { this.jwtUtil = jwtUtil; } + @Operation(summary = "새 댓글 등록", description = "질문에 대한 새 댓글을 등록합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글이 성공적으로 등록됨"), + @ApiResponse(responseCode = "400", description = "잘못된 입력"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) @PostMapping public ResponseEntity registReply( @RequestBody ReplyRegistRequestDTO replyRegistRequestDTO, @@ -32,18 +41,36 @@ public ResponseEntity registReply( return ResponseEntity.ok(reply); } + @Operation(summary = "PK로 댓글 조회", description = "댓글 ID, 즉 PK를 통해 댓글을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글을 성공적으로 찾음"), + @ApiResponse(responseCode = "401", description = "인증되지 않음"), + @ApiResponse(responseCode = "404", description = "댓글을 찾을 수 없음") + }) @GetMapping("/{replyId}") public ResponseEntity findReplyByReplyId(@PathVariable Long replyId) { Reply reply = replyService.findReplyByReplyId(replyId); return reply != null ? ResponseEntity.ok(reply) : ResponseEntity.notFound().build(); } + @Operation(summary = "질문 ID로 댓글 목록 조회", description = "질문 ID로 연결된 모든 댓글을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글을 성공적으로 찾음"), + @ApiResponse(responseCode = "401", description = "인증되지 않음"), + @ApiResponse(responseCode = "404", description = "댓글을 찾을 수 없음") + }) @GetMapping("/question/{questionId}") public ResponseEntity> findRepliesByQuestionId(@PathVariable Long questionId) { List replies = replyService.findRepliesByQuestionId(questionId); return ResponseEntity.ok(replies); } + @Operation(summary = "계정 ID로 댓글 목록 조회", description = "인증된 계정과 연결된 모든 댓글을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글 목록을 성공적으로 찾음"), + @ApiResponse(responseCode = "401", description = "인증되지 않음"), + @ApiResponse(responseCode = "404", description = "댓글을 찾을 수 없음") + }) @GetMapping("/account/{accountId}") public ResponseEntity> findRepliesByAccountId( @RequestHeader(name = "Authorization") String token) throws Exception { @@ -53,6 +80,12 @@ public ResponseEntity> findRepliesByAccountId( return ResponseEntity.ok(replies); } + @Operation(summary = "댓글 수정", description = "제공된 데이터를 기반으로 특정 댓글을 수정합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글이 성공적으로 수정됨"), + @ApiResponse(responseCode = "400", description = "잘못된 수정 데이터"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) @PatchMapping public ResponseEntity updateReply( @RequestBody ReplyUpdateRequestDTO replyUpdateRequestDTO, @@ -67,6 +100,12 @@ public ResponseEntity updateReply( } } + @Operation(summary = "댓글 삭제", description = "댓글 ID로 댓글을 삭제합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "댓글이 성공적으로 삭제됨"), + @ApiResponse(responseCode = "400", description = "잘못된 파라미터"), + @ApiResponse(responseCode = "401", description = "인증되지 않음") + }) @DeleteMapping("/{replyId}") public ResponseEntity deleteReply( @PathVariable Long replyId) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 37d28f2..d41b934 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,21 +32,17 @@ spring: properties: hibernate: format_sql: true -# web: -# cors: -# allow-credentials: true -# allowed-origin-patterns: -# - "http://localhost:8080" -# - "http://localhost:5173" -# allowed-methods: -# - GET -# - POST -# - PUT -# - PATCH -# - DELETE -# - OPTIONS -# allowed-headers: "*" -# +springdoc: + api-docs: + path: /v3/api-docs + swagger-ui: + path: /swagger-ui.html + display-operation-id: true # 메서드 ID 표시 + default-model-expand-depth: 1 # 모델 깊이 + doc-expansion: none # 기본 확장 상태 (none, list, full) + show-extensions: true # 확장 정보 표시 + operationsSorter: alpha # 알파벳순 정렬 + tagsSorter: alpha # 태그 알파벳순 정렬 logging: level: org: From 1b13669df5fc9ec928745d45e2a44ebd4dd5c5b8 Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Fri, 11 Oct 2024 14:46:37 +0900 Subject: [PATCH 4/6] =?UTF-8?q?Swagger=20=EC=84=A4=EB=AA=85=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EA=B5=AC=ED=98=84=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 +- .../devlinkbackend/config/CorsConfig.java | 1 + .../config/JwtAuthenticationFilter.java | 28 +++++------ .../devlinkbackend/config/SecurityConfig.java | 49 ++++++++++--------- .../devlinkbackend/ether/entity/Ether.java | 8 ++- 5 files changed, 49 insertions(+), 41 deletions(-) diff --git a/build.gradle b/build.gradle index b72d4b2..47d9ccc 100644 --- a/build.gradle +++ b/build.gradle @@ -23,14 +23,14 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'me.paulschwarz:spring-dotenv:4.0.0' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' // 3.3.x 버전으로 변경 없음 - implementation 'com.nimbusds:nimbus-jose-jwt:9.37.2' implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' // 또는 jjwt-gson 사용 시 'io.jsonwebtoken:jjwt-gson:0.11.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' // Spring Boot 3.3.4에 맞는 2.x 버전 compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' - developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.mysql:mysql-connector-j' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' diff --git a/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java index 47448ff..1cecdac 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java @@ -13,6 +13,7 @@ public class CorsConfig { public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); + configuration.addAllowedOrigin("http://125.132.216.190:15530"); configuration.addAllowedOrigin("http://localhost:8080"); configuration.addAllowedOrigin("http://localhost:5173"); // 테스트에서 사용되는 도메인 추가 diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java index 80ba081..c497b64 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java @@ -47,23 +47,22 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // 새로 발급된 액세스 토큰을 Authorization 헤더에 추가 response.setHeader("Authorization", "Bearer " + token); } else { - System.out.println("refrehToken으로 AccessToken 발급하려다가 refreshToken 없어서 실패"); + System.out.println("refrehToken으로 accessToken 발급하려다가 refreshToken 없어서 실패"); + return; } } - if (token != null) { // refreshToken도 없어 AccessToken이 아예 없는 경우 지나가기 - try { - // 토큰 검증 | 검증 성공 시 SecurityContext에 인증 정보 저장 - String userPrincipal = jwtUtil.getSubjectFromTokenWithAuth(token); - UsernamePasswordAuthenticationToken authentication = - new UsernamePasswordAuthenticationToken(userPrincipal, null, null); + try { + // 토큰 검증 | 검증 성공 시 SecurityContext에 인증 정보 저장 + String userPrincipal = jwtUtil.getSubjectFromTokenWithAuth(token); + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken(userPrincipal, null, null); - SecurityContextHolder.getContext().setAuthentication(authentication); - } catch (Exception e) { - // 검증 실패 시 401 에러 설정 - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (Exception e) { + // 검증 실패 시 401 에러 설정 + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; } // 필터 체인 진행 @@ -79,7 +78,8 @@ protected boolean shouldNotFilter(HttpServletRequest request) { || path.equals("/swagger-ui.html") || path.startsWith("/swagger-resources") || path.startsWith("/webjars") - || path.startsWith("/login"); + || path.startsWith("/login") + || path.startsWith("/**"); } // 쿠키에서 리프레시 토큰을 추출하는 메서드 diff --git a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java index 60aa9ee..6bf3767 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java @@ -51,7 +51,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ) .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers( - "/", + "/**", + "/api/auth/epicgames/callback", // 토큰 호출 부분 "/login", "/v3/api-docs/**", // Swagger API Docs 경로 "/swagger-ui/**", // Swagger UI 정적 리소스 경로 @@ -64,6 +65,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ).permitAll() // Swagger 관련 경로 허용 .anyRequest().authenticated() ) + // oauth2Login 설정이 다른 경로에서만 작동하도록 설정 .oauth2Login(oauth2Login -> oauth2Login .loginPage("/login") @@ -74,12 +76,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ) .successHandler(oauth2AuthenticationSuccessHandler()) ) - .logout(logout -> logout - .logoutUrl("/logout") - .addLogoutHandler(logoutHandler()) - .logoutSuccessUrl("/") - ) +// .logout(logout -> logout +// .logoutUrl("/logout") +// .addLogoutHandler(logoutHandler()) +// .logoutSuccessUrl("/") +// ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + return http.build(); } @@ -119,21 +122,21 @@ public AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler() { }; } - @Bean - public LogoutHandler logoutHandler() { - return (HttpServletRequest request, HttpServletResponse response, Authentication authentication) -> { - // 쿠키 삭제 - deleteCookie(response, "access_token"); - deleteCookie(response, "refresh_token"); - }; - } - - private void deleteCookie(HttpServletResponse response, String cookieName) { - Cookie cookie = new Cookie(cookieName, null); - cookie.setPath("/"); - cookie.setMaxAge(0); // 쿠키 즉시 삭제 - cookie.setHttpOnly(true); - cookie.setSecure(true); - response.addCookie(cookie); - } +// @Bean +// public LogoutHandler logoutHandler() { +// return (HttpServletRequest request, HttpServletResponse response, Authentication authentication) -> { +// // 쿠키 삭제 +// deleteCookie(response, "access_token"); +// deleteCookie(response, "refresh_token"); +// }; +// } +// +// private void deleteCookie(HttpServletResponse response, String cookieName) { +// Cookie cookie = new Cookie(cookieName, null); +// cookie.setPath("/"); +// cookie.setMaxAge(0); // 쿠키 즉시 삭제 +// cookie.setHttpOnly(true); +// cookie.setSecure(true); +// response.addCookie(cookie); +// } } \ No newline at end of file diff --git a/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java b/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java index f9ab298..0c8464a 100644 --- a/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java +++ b/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java @@ -1,7 +1,8 @@ package com.mtvs.devlinkbackend.ether.entity; import jakarta.persistence.*; -import lombok.NoArgsConstructor; +import lombok.Getter; +import lombok.ToString; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; @@ -9,7 +10,8 @@ @Table(name = "ETHER") @Entity(name = "ETHER") -@NoArgsConstructor +@Getter +@ToString public class Ether { @Id @@ -34,6 +36,8 @@ public class Ether { @Column(name = "ACCOUNT_ID") private String accountId; + public Ether () {} + public Ether(String accountId, String reason, Long amount) { this.accountId = accountId; this.reason = reason; From 4c828ff92358fd098905a3733178d3ebef37137d Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Sat, 12 Oct 2024 14:10:42 +0900 Subject: [PATCH 5/6] =?UTF-8?q?Refactor:=20Oauth2Controller=20accessToken?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD=20API=20Swagger=EC=97=90=20Parameter=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/Oauth2UserController.java | 5 +++-- .../oauth2/dto/EpicGamesCallbackRequestDTO.java | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/mtvs/devlinkbackend/oauth2/dto/EpicGamesCallbackRequestDTO.java diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java index 25a333f..6dcfb3c 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java @@ -1,5 +1,6 @@ package com.mtvs.devlinkbackend.oauth2.controller; +import com.mtvs.devlinkbackend.oauth2.dto.EpicGamesCallbackRequestDTO; import com.mtvs.devlinkbackend.oauth2.service.EpicGamesTokenService; import com.mtvs.devlinkbackend.oauth2.service.UserService; import io.swagger.v3.oas.annotations.Operation; @@ -79,9 +80,9 @@ public ResponseEntity getEpicGamesUserInfo( @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), }) public ResponseEntity handleEpicGamesCallback( - @RequestBody Map payload, HttpServletResponse response) { + @RequestBody EpicGamesCallbackRequestDTO payload, HttpServletResponse response) { - String code = payload.get("code"); + String code = payload.getCode(); Map tokenBody = epicGamesTokenService.getAccessTokenAndRefreshTokenByCode(code); diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/dto/EpicGamesCallbackRequestDTO.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/dto/EpicGamesCallbackRequestDTO.java new file mode 100644 index 0000000..07f535f --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/dto/EpicGamesCallbackRequestDTO.java @@ -0,0 +1,13 @@ +package com.mtvs.devlinkbackend.oauth2.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +@Getter @Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class EpicGamesCallbackRequestDTO { + @Schema(description = "Authorization code returned from Epic Games", example = "cfd1de1a8d224203b0445fe977838d81") + private String code; +} From 742978d9c56deb266ee67b34fc28e164bf28371e Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Sat, 12 Oct 2024 14:11:42 +0900 Subject: [PATCH 6/6] =?UTF-8?q?Refactor:=20=EC=9D=B8=EC=A6=9D=20=EC=97=94?= =?UTF-8?q?=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=EC=97=90=EC=84=9C=20**=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java index 6bf3767..de37e30 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java @@ -51,7 +51,6 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ) .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers( - "/**", "/api/auth/epicgames/callback", // 토큰 호출 부분 "/login", "/v3/api-docs/**", // Swagger API Docs 경로