From d67070577b3fffa74c4955ee98f5e2685b5d2604 Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Mon, 14 Oct 2024 09:25:04 +0900 Subject: [PATCH 01/10] =?UTF-8?q?Feat:=20Team=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80/=EC=82=AD=EC=A0=9C=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/controller/TeamController.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/java/com/mtvs/devlinkbackend/team/controller/TeamController.java b/src/main/java/com/mtvs/devlinkbackend/team/controller/TeamController.java index 5e2d407..4153b07 100644 --- a/src/main/java/com/mtvs/devlinkbackend/team/controller/TeamController.java +++ b/src/main/java/com/mtvs/devlinkbackend/team/controller/TeamController.java @@ -1,6 +1,9 @@ package com.mtvs.devlinkbackend.team.controller; import com.mtvs.devlinkbackend.config.JwtUtil; +import com.mtvs.devlinkbackend.guild.dto.GuildMemberModifyRequestDTO; +import com.mtvs.devlinkbackend.guild.entity.Guild; +import com.mtvs.devlinkbackend.team.dto.TeamMemberModifyRequestDTO; import com.mtvs.devlinkbackend.team.dto.TeamRegistRequestDTO; import com.mtvs.devlinkbackend.team.dto.TeamUpdateRequestDTO; import com.mtvs.devlinkbackend.team.entity.Team; @@ -112,6 +115,36 @@ public ResponseEntity updateTeam( } } + @Operation(summary = "팀 멤버 추가", description = "길드에 멤버를 추가합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "멤버가 성공적으로 추가되었습니다."), + @ApiResponse(responseCode = "403", description = "추가 권한이 없습니다."), + @ApiResponse(responseCode = "404", description = "길드를 찾을 수 없습니다.") + }) + @PostMapping("/member") + public Team addMemberToTeam( + @RequestBody TeamMemberModifyRequestDTO teamMemberModifyRequestDTO, + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + return teamService.addMemberToTeam(teamMemberModifyRequestDTO, accountId); + } + + @Operation(summary = "팀 멤버 제거", description = "길드에서 멤버를 제거합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "멤버가 성공적으로 제거되었습니다."), + @ApiResponse(responseCode = "403", description = "제거 권한이 없습니다."), + @ApiResponse(responseCode = "404", description = "길드를 찾을 수 없습니다.") + }) + @DeleteMapping("/member") + public Team removeMemberFromTeam( + @RequestBody TeamMemberModifyRequestDTO teamMemberModifyRequestDTO, + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + return teamService.removeMemberToTeam(teamMemberModifyRequestDTO, accountId); + } + @Operation(summary = "팀 삭제", description = "ID를 통해 팀을 삭제합니다.") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "팀이 성공적으로 삭제되었습니다."), From fe3d1486dc8cba208b38497c76d4d6b6aa5f168e Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Mon, 14 Oct 2024 09:33:59 +0900 Subject: [PATCH 02/10] =?UTF-8?q?Test:=20Team=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80/=EC=82=AD=EC=A0=9C=20TestCode=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devlinkbackend/crud/TeamCRUDTest.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/mtvs/devlinkbackend/crud/TeamCRUDTest.java b/src/test/java/com/mtvs/devlinkbackend/crud/TeamCRUDTest.java index 1c81c3c..2547da1 100644 --- a/src/test/java/com/mtvs/devlinkbackend/crud/TeamCRUDTest.java +++ b/src/test/java/com/mtvs/devlinkbackend/crud/TeamCRUDTest.java @@ -1,5 +1,6 @@ package com.mtvs.devlinkbackend.crud; +import com.mtvs.devlinkbackend.team.dto.TeamMemberModifyRequestDTO; import com.mtvs.devlinkbackend.team.dto.TeamRegistRequestDTO; import com.mtvs.devlinkbackend.team.dto.TeamUpdateRequestDTO; import com.mtvs.devlinkbackend.team.service.TeamService; @@ -31,13 +32,20 @@ private static Stream newTeam() { ); } - private static Stream modifiedTeam() { + private static Stream updatedTeam() { return Stream.of( Arguments.of(new TeamUpdateRequestDTO(1L,"팀 이름0", "소개0", List.of("계정2","계정3")), "계정1"), Arguments.of(new TeamUpdateRequestDTO(2L,"팀 이름00", "소개00", List.of("계정1","계정3")), "계정2") ); } + private static Stream modifiedTeam() { + return Stream.of( + Arguments.of(new TeamMemberModifyRequestDTO(1L, List.of("계정2","계정3")), "계정1"), + Arguments.of(new TeamMemberModifyRequestDTO(2L, List.of("계정3","계정4")), "계정2") + ); + } + @DisplayName("팀 추가 테스트") @ParameterizedTest @MethodSource("newTeam") @@ -83,7 +91,7 @@ public void testFindTeamsByTeamNameContaining(String teamName) { } @DisplayName("팀 수정 테스트") - @MethodSource("modifiedTeam") + @MethodSource("updatedTeam") @ParameterizedTest @Order(5) public void testUpdateTeam(TeamUpdateRequestDTO questionUpdateRequestDTO, String accountId) { @@ -91,10 +99,28 @@ public void testUpdateTeam(TeamUpdateRequestDTO questionUpdateRequestDTO, String System.out.println(teamService.updateTeam(questionUpdateRequestDTO, accountId))); } + @DisplayName("팀 멤버 추가 테스트") + @MethodSource("modifiedTeam") + @ParameterizedTest + @Order(5) + public void testAddMemberToTeam(TeamMemberModifyRequestDTO teamMemberModifyRequestDTO, String accountId) { + Assertions.assertDoesNotThrow(() -> + System.out.println(teamService.addMemberToTeam(teamMemberModifyRequestDTO, accountId))); + } + + @DisplayName("팀 멤버 삭제 테스트") + @MethodSource("modifiedTeam") + @ParameterizedTest + @Order(5) + public void testRemoveMemberToTeam(TeamMemberModifyRequestDTO teamMemberModifyRequestDTO, String accountId) { + Assertions.assertDoesNotThrow(() -> + System.out.println(teamService.removeMemberToTeam(teamMemberModifyRequestDTO, accountId))); + } + @DisplayName("팀 삭제 테스트") @ValueSource(longs = {0,1}) @ParameterizedTest - @Order(6) + @Order(8) public void testDeleteTeam(long teamId) { Assertions.assertDoesNotThrow(() -> teamService.deleteTeam(teamId)); From a179bbaf06984ebba21ba0dc3fe094b2067cc1d4 Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Mon, 14 Oct 2024 11:40:06 +0900 Subject: [PATCH 03/10] =?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 --- .../channel/entity/Channel.java | 56 +++++++++++++++++++ .../channel/repository/ChannelRepository.java | 9 +++ .../config/JwtAuthenticationFilter.java | 19 ++----- 3 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java b/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java new file mode 100644 index 0000000..3610d9d --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java @@ -0,0 +1,56 @@ +package com.mtvs.devlinkbackend.channel.entity; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.*; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Table(name = "CHANNEL") +@Entity(name = "Channel") +@ToString +@NoArgsConstructor +public class Channel { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "CHANNEL_ID") + private Long channelId; + + @Column(name = "JSON_DATA", columnDefinition = "TEXT") + private String jsonData; + + @Transient + private List> dataList; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @CreationTimestamp + @Column(name = "CREATED_AT") + private LocalDateTime createdAt; + + @UpdateTimestamp + @Column(name = "MODIFIED_AT") + private LocalDateTime modifiedAt; + + public Long getChannelId() { + return channelId; + } + + public List> getDataList() throws JsonProcessingException { + if (this.dataList == null && this.jsonData != null) { + this.dataList = objectMapper.readValue(this.jsonData, List.class); + } + return this.dataList; + } + + public void setDataList(List> dataList) throws JsonProcessingException { + this.dataList = dataList; + this.jsonData = objectMapper.writeValueAsString(dataList); + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java b/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java new file mode 100644 index 0000000..d1cf28e --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java @@ -0,0 +1,9 @@ +package com.mtvs.devlinkbackend.channel.repository; + +import com.mtvs.devlinkbackend.channel.entity.Channel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ChannelRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java index d0034d8..a676f97 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java @@ -38,18 +38,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse if (StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith("Bearer ")) { token = authorizationHeader.substring(7); // "Bearer " 이후의 토큰 부분 추출 } else { - // 헤더에 액세스 토큰이 없는 경우, 쿠키에서 리프레시 토큰 추출 - token = getRefreshTokenFromCookies(request); - if (token != null) { - // 리프레시 토큰으로 새 액세스 토큰 발급 - token = epicGamesTokenService.getAccessTokenByRefreshToken(token); - - // 새로 발급된 액세스 토큰을 Authorization 헤더에 추가 - response.setHeader("Authorization", "Bearer " + token); - } else { - System.out.println("refrehToken으로 accessToken 발급하려다가 refreshToken 없어서 실패"); - return; - } + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 잘못된 인증 헤더 + return; } try { @@ -61,7 +51,10 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { // 검증 실패 시 401 에러 설정 - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + if(e.getMessage().equals("JWT is expired")) + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + else + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } From 6a51a9e23190aa372f33bac55734592053ad34af Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Tue, 15 Oct 2024 09:46:25 +0900 Subject: [PATCH 04/10] =?UTF-8?q?Refactor:=20JwtAuth=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/JwtAuthenticationFilter.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java index a676f97..20b94c0 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java @@ -1,6 +1,8 @@ package com.mtvs.devlinkbackend.config; +import com.mtvs.devlinkbackend.oauth2.entity.User; import com.mtvs.devlinkbackend.oauth2.service.EpicGamesTokenService; +import com.mtvs.devlinkbackend.oauth2.service.UserService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; @@ -17,11 +19,11 @@ @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtUtil jwtUtil; - private final EpicGamesTokenService epicGamesTokenService; + private final UserService userService; - public JwtAuthenticationFilter(JwtUtil jwtUtil, EpicGamesTokenService epicGamesTokenService) { + public JwtAuthenticationFilter(JwtUtil jwtUtil, UserService userService) { this.jwtUtil = jwtUtil; - this.epicGamesTokenService = epicGamesTokenService; + this.userService = userService; } @Override @@ -44,11 +46,18 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse try { // 토큰 검증 | 검증 성공 시 SecurityContext에 인증 정보 저장 - String userPrincipal = jwtUtil.getSubjectFromTokenWithAuth(token); + User user = userService.findUserByAccessToken(token); + String accountId = jwtUtil.getSubjectFromTokenWithAuth(token); UsernamePasswordAuthenticationToken authentication = - new UsernamePasswordAuthenticationToken(userPrincipal, null, null); + new UsernamePasswordAuthenticationToken(accountId, null, null); SecurityContextHolder.getContext().setAuthentication(authentication); + + if(user == null) { + response.setStatus(HttpServletResponse.SC_ACCEPTED); + } else + response.setStatus(HttpServletResponse.SC_OK); + } catch (Exception e) { // 검증 실패 시 401 에러 설정 if(e.getMessage().equals("JWT is expired")) From 3dab945c20e4bf44733951d13b1984fa2f3edc5c Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Tue, 15 Oct 2024 09:58:50 +0900 Subject: [PATCH 05/10] =?UTF-8?q?Feat:=20JwtAuth=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B6=84=ED=95=A0=20=EB=B0=8F=20Login=20API=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/JwtAuthenticationFilter.java | 10 ++-------- .../controller/Oauth2UserController.java | 19 ++++++++++++++++++- .../oauth2/service/UserService.java | 4 ++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java index 20b94c0..e3818d6 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java @@ -40,30 +40,24 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse if (StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith("Bearer ")) { token = authorizationHeader.substring(7); // "Bearer " 이후의 토큰 부분 추출 } else { - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 잘못된 인증 헤더 + response.setStatus(450); // 잘못된 인증 헤더 return; } try { // 토큰 검증 | 검증 성공 시 SecurityContext에 인증 정보 저장 - User user = userService.findUserByAccessToken(token); String accountId = jwtUtil.getSubjectFromTokenWithAuth(token); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(accountId, null, null); SecurityContextHolder.getContext().setAuthentication(authentication); - if(user == null) { - response.setStatus(HttpServletResponse.SC_ACCEPTED); - } else - response.setStatus(HttpServletResponse.SC_OK); - } catch (Exception e) { // 검증 실패 시 401 에러 설정 if(e.getMessage().equals("JWT is expired")) response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); else - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + response.setStatus(449); // 헤더에 들어 있는 토큰이 잘못됨 return; } 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 6dcfb3c..30a4713 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java @@ -1,6 +1,7 @@ package com.mtvs.devlinkbackend.oauth2.controller; import com.mtvs.devlinkbackend.oauth2.dto.EpicGamesCallbackRequestDTO; +import com.mtvs.devlinkbackend.oauth2.entity.User; import com.mtvs.devlinkbackend.oauth2.service.EpicGamesTokenService; import com.mtvs.devlinkbackend.oauth2.service.UserService; import io.swagger.v3.oas.annotations.Operation; @@ -40,7 +41,7 @@ public ResponseEntity getLocalUserInfo(@RequestHeader("Authorization") String try { String token = extractToken(authorizationHeader); - return ResponseEntity.ok(userService.findUserByAccessToken(token)); + return ResponseEntity.ok(userService.findUserByAuthorizationHeader(token)); } catch (Exception e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token"); } @@ -107,6 +108,22 @@ public ResponseEntity handleEpicGamesCallback( return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } + @PostMapping("/login") + public ResponseEntity authLogin( + @RequestHeader(name = "Authorization") String authorizationHeader) { + User user = userService.findUserByAuthorizationHeader(authorizationHeader); + + // 222 : 해당 User는 이미 서비스를 사용한 경험이 있음 + // 260 : 해당 User가 처음 서비스를 사용 + return user != null ? ResponseEntity.status(222).build() : ResponseEntity.status(260).build(); + } + + @PatchMapping("/local/user-info") + public ResponseEntity updateLocalUserInfo() { + // User 추가 정보 확정되면 개발 예정 + return ResponseEntity.ok().build(); + } + private String extractToken(String authorizationHeader) { if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { return authorizationHeader.substring(7); diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java index fc394d2..3f8a084 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java @@ -28,9 +28,9 @@ public User registUserByAccessToken(String accessToken) { } } - public User findUserByAccessToken(String accessToken) { + public User findUserByAuthorizationHeader(String authorizationHeader) { try { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(accessToken); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); return userRepository.findUserByAccountId(accountId); } catch (Exception e) { throw new RuntimeException(e); From 2c549b5e3f99fdeb4e47a2aea7db5c1b8a316e55 Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Tue, 15 Oct 2024 13:11:37 +0900 Subject: [PATCH 06/10] =?UTF-8?q?Feat:=20login=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../devlinkbackend/oauth2/controller/Oauth2UserController.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 47d9ccc..087b383 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' implementation 'me.paulschwarz:spring-dotenv:4.0.0' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' // 3.3.x 버전으로 변경 없음 implementation 'io.jsonwebtoken:jjwt-api:0.11.2' 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 30a4713..4ce2347 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java @@ -115,7 +115,7 @@ public ResponseEntity authLogin( // 222 : 해당 User는 이미 서비스를 사용한 경험이 있음 // 260 : 해당 User가 처음 서비스를 사용 - return user != null ? ResponseEntity.status(222).build() : ResponseEntity.status(260).build(); + return user != null ? ResponseEntity.status(222).body("Existing User") : ResponseEntity.status(260).body("New User"); } @PatchMapping("/local/user-info") From 2f2336f3af3163b2187276048ccb3fcad5da2bd4 Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Tue, 15 Oct 2024 13:32:08 +0900 Subject: [PATCH 07/10] =?UTF-8?q?Feat:=20MongoDB=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mtvs/devlinkbackend/channel/entity/Channel.java | 8 ++------ .../channel/repository/ChannelRepository.java | 3 ++- src/main/resources/application.yml | 13 ++++++++++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java b/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java index 3610d9d..0c0dced 100644 --- a/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java +++ b/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java @@ -7,19 +7,15 @@ import lombok.ToString; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; +import org.springframework.data.mongodb.core.mapping.Document; import java.time.LocalDateTime; import java.util.List; import java.util.Map; -@Table(name = "CHANNEL") -@Entity(name = "Channel") -@ToString -@NoArgsConstructor +@Document(collation = "CHANNEL") public class Channel { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "CHANNEL_ID") private Long channelId; @Column(name = "JSON_DATA", columnDefinition = "TEXT") diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java b/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java index d1cf28e..7aaa289 100644 --- a/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java +++ b/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java @@ -2,8 +2,9 @@ import com.mtvs.devlinkbackend.channel.entity.Channel; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; @Repository -public interface ChannelRepository extends JpaRepository { +public interface ChannelRepository extends MongoRepository { } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d41b934..bbb133d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -21,9 +21,16 @@ spring: user-name-attribute: sub datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3306/devlink - username: root - password: 1234 + url: jdbc:mysql://125.132.216.190:15531/devlink + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} + data: + mongodb: + host: 125.132.216.190 + port: 15532 + database: devlink + username: ${MONGO_USERNAME} + password: ${MONGO_PASSWORD} jpa: show-sql: true database: mysql From b9ed14e305c7e2b97a810d553687d7cc9567a101 Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Tue, 15 Oct 2024 16:13:05 +0900 Subject: [PATCH 08/10] =?UTF-8?q?Feat:=20Channel=20CRUD=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DevlinkBackendApplication.java | 2 + .../channel/controller/ChannelController.java | 71 +++++++++++++++++++ .../channel/dto/ChannelRegistRequestDTO.java | 16 +++++ .../channel/dto/ChannelUpdateRequestDTO.java | 17 +++++ .../channel/entity/Channel.java | 52 ++++++-------- .../channel/entity/PositionType.java | 33 +++++++++ .../channel/repository/ChannelRepository.java | 4 +- .../channel/service/ChannelService.java | 55 ++++++++++++++ 8 files changed, 218 insertions(+), 32 deletions(-) create mode 100644 src/main/java/com/mtvs/devlinkbackend/channel/controller/ChannelController.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/channel/dto/ChannelRegistRequestDTO.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/channel/dto/ChannelUpdateRequestDTO.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/channel/entity/PositionType.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/channel/service/ChannelService.java diff --git a/src/main/java/com/mtvs/devlinkbackend/DevlinkBackendApplication.java b/src/main/java/com/mtvs/devlinkbackend/DevlinkBackendApplication.java index a04d1a6..26aa236 100644 --- a/src/main/java/com/mtvs/devlinkbackend/DevlinkBackendApplication.java +++ b/src/main/java/com/mtvs/devlinkbackend/DevlinkBackendApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.mongodb.config.EnableMongoAuditing; @SpringBootApplication +@EnableMongoAuditing public class DevlinkBackendApplication { public static void main(String[] args) { diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/controller/ChannelController.java b/src/main/java/com/mtvs/devlinkbackend/channel/controller/ChannelController.java new file mode 100644 index 0000000..6c68244 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/channel/controller/ChannelController.java @@ -0,0 +1,71 @@ +package com.mtvs.devlinkbackend.channel.controller; + +import com.mtvs.devlinkbackend.channel.dto.ChannelRegistRequestDTO; +import com.mtvs.devlinkbackend.channel.dto.ChannelUpdateRequestDTO; +import com.mtvs.devlinkbackend.channel.entity.Channel; +import com.mtvs.devlinkbackend.channel.service.ChannelService; +import com.mtvs.devlinkbackend.config.JwtUtil; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/channel") +public class ChannelController { + private final ChannelService channelService; + private final JwtUtil jwtUtil; + + public ChannelController(ChannelService channelService, JwtUtil jwtUtil) { + this.channelService = channelService; + this.jwtUtil = jwtUtil; + } + + // 새 채널 생성 + @PostMapping + public ResponseEntity createChannel( + @RequestBody ChannelRegistRequestDTO channelRegistRequestDTO, + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + Channel savedChannel = channelService.saveChannel(channelRegistRequestDTO, accountId); + return ResponseEntity.ok(savedChannel); + } + + // 모든 채널 조회 + @GetMapping + public ResponseEntity> getAllChannels() { + List channels = channelService.getAllChannels(); + return ResponseEntity.ok(channels); + } + + // 특정 채널 조회 + @GetMapping("/{channelId}") + public ResponseEntity getChannelById(@PathVariable String channelId) { + return channelService.getChannelById(channelId) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + // 채널 업데이트 + @PatchMapping + public ResponseEntity updateChannel( + @RequestBody ChannelUpdateRequestDTO channelUpdateRequestDTO, + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + try { + Channel updatedChannel = channelService.updateChannel(channelUpdateRequestDTO, accountId); + return ResponseEntity.ok(updatedChannel); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(null); + } + } + + // 채널 삭제 + @DeleteMapping("/{channelId}") + public ResponseEntity deleteChannel(@PathVariable String channelId) { + channelService.deleteChannel(channelId); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/dto/ChannelRegistRequestDTO.java b/src/main/java/com/mtvs/devlinkbackend/channel/dto/ChannelRegistRequestDTO.java new file mode 100644 index 0000000..b26bbf6 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/channel/dto/ChannelRegistRequestDTO.java @@ -0,0 +1,16 @@ +package com.mtvs.devlinkbackend.channel.dto; + +import com.mtvs.devlinkbackend.channel.entity.PositionType; +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +@Data +public class ChannelRegistRequestDTO { + private List positionTypes; +} \ No newline at end of file diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/dto/ChannelUpdateRequestDTO.java b/src/main/java/com/mtvs/devlinkbackend/channel/dto/ChannelUpdateRequestDTO.java new file mode 100644 index 0000000..a55268f --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/channel/dto/ChannelUpdateRequestDTO.java @@ -0,0 +1,17 @@ +package com.mtvs.devlinkbackend.channel.dto; + +import com.mtvs.devlinkbackend.channel.entity.PositionType; +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +@Data +public class ChannelUpdateRequestDTO { + private String channelId; + private List positionTypes; +} \ No newline at end of file diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java b/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java index 0c0dced..1f0c9f2 100644 --- a/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java +++ b/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java @@ -1,52 +1,44 @@ package com.mtvs.devlinkbackend.channel.entity; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.*; +import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; -import org.hibernate.annotations.CreationTimestamp; -import org.hibernate.annotations.UpdateTimestamp; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; import java.time.LocalDateTime; import java.util.List; -import java.util.Map; +import java.util.UUID; -@Document(collation = "CHANNEL") +@Data +@NoArgsConstructor +@ToString +@Document(collation = "channel") public class Channel { @Id - private Long channelId; + private String channelId; - @Column(name = "JSON_DATA", columnDefinition = "TEXT") - private String jsonData; + @Field(name = "owner_id") + private String ownerId; - @Transient - private List> dataList; + private List positionTypes; // 여러 개의 position과 type 저장 - private static final ObjectMapper objectMapper = new ObjectMapper(); - - @CreationTimestamp - @Column(name = "CREATED_AT") + @CreatedDate private LocalDateTime createdAt; - @UpdateTimestamp - @Column(name = "MODIFIED_AT") + @LastModifiedDate private LocalDateTime modifiedAt; - public Long getChannelId() { - return channelId; - } - - public List> getDataList() throws JsonProcessingException { - if (this.dataList == null && this.jsonData != null) { - this.dataList = objectMapper.readValue(this.jsonData, List.class); - } - return this.dataList; + public Channel(String ownerId, List positionTypes) { + this.channelId = UUID.randomUUID().toString(); + this.ownerId = ownerId; + this.positionTypes = positionTypes; } - public void setDataList(List> dataList) throws JsonProcessingException { - this.dataList = dataList; - this.jsonData = objectMapper.writeValueAsString(dataList); + public void setPositionTypes(List positionTypes) { + this.positionTypes = positionTypes; } -} +} \ No newline at end of file diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/entity/PositionType.java b/src/main/java/com/mtvs/devlinkbackend/channel/entity/PositionType.java new file mode 100644 index 0000000..0291bfd --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/channel/entity/PositionType.java @@ -0,0 +1,33 @@ +package com.mtvs.devlinkbackend.channel.entity; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.mongodb.core.mapping.Field; + +@Data +@NoArgsConstructor +public class PositionType { + private Position position; + + @Field("type") + private String type; + + public PositionType(Position position, String type) { + this.position = position; + this.type = type; + } + + @Data + @NoArgsConstructor + public static class Position { + private int x; + private int y; + private int z; + + public Position(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java b/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java index 7aaa289..d642c9d 100644 --- a/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java +++ b/src/main/java/com/mtvs/devlinkbackend/channel/repository/ChannelRepository.java @@ -1,10 +1,10 @@ package com.mtvs.devlinkbackend.channel.repository; import com.mtvs.devlinkbackend.channel.entity.Channel; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; @Repository -public interface ChannelRepository extends MongoRepository { +public interface ChannelRepository extends MongoRepository { + } \ No newline at end of file diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/service/ChannelService.java b/src/main/java/com/mtvs/devlinkbackend/channel/service/ChannelService.java new file mode 100644 index 0000000..1b5f65a --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/channel/service/ChannelService.java @@ -0,0 +1,55 @@ +package com.mtvs.devlinkbackend.channel.service; + +import com.mtvs.devlinkbackend.channel.dto.ChannelRegistRequestDTO; +import com.mtvs.devlinkbackend.channel.dto.ChannelUpdateRequestDTO; +import com.mtvs.devlinkbackend.channel.entity.Channel; +import com.mtvs.devlinkbackend.channel.repository.ChannelRepository; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class ChannelService { + private final ChannelRepository channelRepository; + + public ChannelService(ChannelRepository channelRepository) { + this.channelRepository = channelRepository; + } + + // 새 채널 저장 + public Channel saveChannel(ChannelRegistRequestDTO channelRegistRequestDTO, String accountId) { + return channelRepository.save(new Channel( + accountId, + channelRegistRequestDTO.getPositionTypes() + )); + } + + // 모든 채널 조회 + + public List getAllChannels() { + return channelRepository.findAll(); + } + + // ID로 특정 채널 조회 + public Optional getChannelById(String channelId) { + return channelRepository.findById(channelId); + } + + // ID로 채널 업데이트 + public Channel updateChannel(ChannelUpdateRequestDTO channelUpdateRequestDTO, String accountId) { + Optional channel = channelRepository.findById(channelUpdateRequestDTO.getChannelId()); + if (channel.isPresent()) { + Channel foundChannel = channel.get(); + if(foundChannel.getOwnerId().equals(accountId)) { + foundChannel.setPositionTypes(channelUpdateRequestDTO.getPositionTypes()); + return foundChannel; + } else throw new IllegalArgumentException("주인이 아닌 다른 사용자 계정으로 채널 수정 시도중"); + } else throw new IllegalArgumentException("요청한 channelId로 해당 채널이 존재하지 않음"); + } + + // ID로 채널 삭제 + public void deleteChannel(String channelId) { + channelRepository.deleteById(channelId); + } +} From 2781996cfb0c53364d8f5c9bb1a045df4567659e Mon Sep 17 00:00:00 2001 From: HEX <123macanic@naver.com> Date: Tue, 15 Oct 2024 23:24:40 +0900 Subject: [PATCH 09/10] =?UTF-8?q?Fix:=20EpicGames=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20API=20=EB=A1=9C=EC=A7=81=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mtvs/devlinkbackend/config/JwtUtil.java | 3 +- .../controller/Oauth2UserController.java | 44 +++-------- .../oauth2/service/EpicGamesTokenService.java | 77 +++++++++---------- .../oauth2/service/UserService.java | 10 ++- src/main/resources/application.yml | 14 +++- 5 files changed, 71 insertions(+), 77 deletions(-) diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtUtil.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtUtil.java index 3b1987b..10cdfbd 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtUtil.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtUtil.java @@ -5,6 +5,7 @@ import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.proc.BadJWTException; @@ -86,7 +87,7 @@ private JWTClaimsSet getClaimsFromToken(String token) throws Exception { SignedJWT signedJWT = SignedJWT.parse(token); JWK jwk = jwkCache.getCachedJWKSet().getKeyByKeyId(signedJWT.getHeader().getKeyID()); - if (jwk == null || !JWSAlgorithm.RS256.equals(jwk.getAlgorithm())) { + if (jwk == null || !JWSAlgorithm.RS256.equals(signedJWT.getHeader().getAlgorithm())) { throw new RuntimeException("JWK key is missing or invalid algorithm"); } 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 4ce2347..1e2ff58 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/controller/Oauth2UserController.java @@ -12,6 +12,7 @@ import org.springframework.http.*; import org.springframework.web.bind.annotation.*; +import java.util.List; import java.util.Map; @RestController @@ -39,9 +40,7 @@ public Oauth2UserController(EpicGamesTokenService epicGamesTokenService, UserSer }) public ResponseEntity getLocalUserInfo(@RequestHeader("Authorization") String authorizationHeader) { try { - String token = extractToken(authorizationHeader); - - return ResponseEntity.ok(userService.findUserByAuthorizationHeader(token)); + return ResponseEntity.ok(userService.findUserByAuthorizationHeader(authorizationHeader)); } catch (Exception e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token"); } @@ -61,9 +60,11 @@ public ResponseEntity getLocalUserInfo(@RequestHeader("Authorization") String public ResponseEntity getEpicGamesUserInfo( @RequestHeader("Authorization") String authorizationHeader) { + System.out.println(authorizationHeader); + try { - Map userAccount = - epicGamesTokenService.getEpicGamesUserAccount(extractToken(authorizationHeader)); + List> userAccount = + epicGamesTokenService.getEpicGamesUserAccount(authorizationHeader); return ResponseEntity.ok(userAccount); } catch (Exception e) { @@ -81,31 +82,16 @@ public ResponseEntity getEpicGamesUserInfo( @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), }) public ResponseEntity handleEpicGamesCallback( - @RequestBody EpicGamesCallbackRequestDTO payload, HttpServletResponse response) { + @RequestBody EpicGamesCallbackRequestDTO payload) { String code = payload.getCode(); Map tokenBody = epicGamesTokenService.getAccessTokenAndRefreshTokenByCode(code); - if (tokenBody != null) { - String accessToken = (String) tokenBody.get("access_token"); - String refreshToken = (String) tokenBody.get("refresh_token"); - - // accessToken과 refreshToken을 쿠키에 담아 return - Cookie accessTokenCookie = new Cookie("access_token", "Bearer " + accessToken); - accessTokenCookie.setMaxAge(15 * 60); - accessTokenCookie.setSecure(true); - Cookie refreshTokenCookie = new Cookie("refresh_token", "Bearer " + refreshToken); - refreshTokenCookie.setMaxAge(3 * 60 * 60); - refreshTokenCookie.setSecure(true); - - response.addCookie(accessTokenCookie); - response.addCookie(refreshTokenCookie); - - return ResponseEntity.status(HttpStatus.CREATED).build(); - } - - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + if (tokenBody != null) + return ResponseEntity.status(HttpStatus.CREATED).body(tokenBody); + else + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } @PostMapping("/login") @@ -123,12 +109,4 @@ public ResponseEntity updateLocalUserInfo() { // User 추가 정보 확정되면 개발 예정 return ResponseEntity.ok().build(); } - - private String extractToken(String authorizationHeader) { - if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { - return authorizationHeader.substring(7); - } else { - throw new IllegalArgumentException("Authorization header must start with 'Bearer '"); - } - } } diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/EpicGamesTokenService.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/EpicGamesTokenService.java index d38da38..85f7f40 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/EpicGamesTokenService.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/EpicGamesTokenService.java @@ -3,6 +3,7 @@ import com.mtvs.devlinkbackend.config.JwtUtil; import com.mtvs.devlinkbackend.oauth2.component.EpicGamesJWKCache; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; @@ -11,17 +12,23 @@ import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.List; import java.util.Map; @Service public class EpicGamesTokenService { - @Value("${spring.security.oauth2.client.registration.epicgames.client-id}") + @Value("${epicgames.registration.client-id}") private String clientId; - @Value("${spring.security.oauth2.client.registration.epicgames.client-secret}") + @Value("${epicgames.registration.client-secret}") private String clientSecret; - private final String redirectUri = ""; + @Value("${epicgames.registration.deployment-id}") + private String deploymentId; + + private final String getAuthorizationCodeURL = "https://www.epicgames.com/id/authorize"; + private final String getAccessTokenURL = "https://api.epicgames.dev/epic/oauth/v2/token"; + private final String getAccountURL = "https://api.epicgames.dev/epic/id/v2/accounts"; private final EpicGamesJWKCache jwkCache; private final JwtUtil jwtUtil; @@ -38,31 +45,6 @@ public Map validateAndParseToken(String token) throws Exception return jwtUtil.getClaimsFromTokenWithoutAuth(token); } - public String getAccessTokenByRefreshToken(String refreshToken) { - // Epic Games의 OAuth2 토큰 엔드포인트 호출 - RestTemplate restTemplate = new RestTemplate(); - HttpHeaders headers = new HttpHeaders(); - - // Basic Authentication 헤더 추가 - String auth = clientId + ":" + clientSecret; - byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(StandardCharsets.UTF_8)); - String authHeader = "Basic " + new String(encodedAuth); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("Authorization", authHeader); - - // 요청 본문 설정 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("grant_type", "refresh_token"); - body.add("token", refreshToken); - - HttpEntity> request = new HttpEntity<>(body, headers); - - // Epic Games 토큰 엔드포인트에 요청 - ResponseEntity response = restTemplate.postForEntity("https://api.epicgames.dev/epic/oauth/v2/token", request, Map.class); - - return (String) response.getBody().get("sub"); - } - public Map getAccessTokenAndRefreshTokenByCode(String code) { // Epic Games의 OAuth2 토큰 엔드포인트 호출 RestTemplate restTemplate = new RestTemplate(); @@ -72,53 +54,66 @@ public Map getAccessTokenAndRefreshTokenByCode(String code) { String auth = clientId + ":" + clientSecret; byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(StandardCharsets.UTF_8)); String authHeader = "Basic " + new String(encodedAuth); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.set("Content-Type", "application/x-www-form-urlencoded"); headers.set("Authorization", authHeader); // 요청 본문 설정 MultiValueMap body = new LinkedMultiValueMap<>(); body.add("grant_type", "authorization_code"); body.add("code", code); - body.add("redirect_uri", redirectUri); + body.add("scope", "basic_profile friends_list presence"); + body.add("deployment_id", deploymentId); HttpEntity> request = new HttpEntity<>(body, headers); // Epic Games 토큰 엔드포인트 요청 - ResponseEntity response = restTemplate.exchange( - "https://api.epicgames.dev/epic/oauth/v2/token", + ResponseEntity> response = restTemplate.exchange( + getAccessTokenURL, HttpMethod.POST, request, - Map.class + new ParameterizedTypeReference>() {} ); return response.getBody(); } - public Map getEpicGamesUserAccount(String accessToken) { + public List> getEpicGamesUserAccount(String authorizationHeader) throws Exception { // Epic Games의 OAuth2 토큰 엔드포인트 호출 RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); // Bearer Authentication 헤더 추가 - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("Authorization", accessToken); + headers.set("Authorization", authorizationHeader); + headers.set("Content-Type", "application/x-www-form-urlencoded"); - HttpEntity> request = new HttpEntity<>(headers); + HttpEntity> request = new HttpEntity<>(new LinkedMultiValueMap<>(), headers); // Epic Games 토큰 엔드포인트 요청 - ResponseEntity response; + ResponseEntity>> response; + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(extractToken(authorizationHeader)); + System.out.println(accountId); try { response = restTemplate.exchange( - "https://api.epicgames.dev/epic/id/v2/accounts?accountId=" + jwtUtil.getClaimsFromTokenWithAuth(accessToken), + getAccountURL + accountId, HttpMethod.GET, request, - Map.class + new ParameterizedTypeReference>>() {} ); + System.out.println(response); } catch (Exception e) { - throw new RuntimeException(e); + e.printStackTrace(); + throw new RuntimeException("API 요청 중 오류 발생 : " + e.getMessage()); } return response.getBody(); } + + private String extractToken(String authorizationHeader) { + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + return authorizationHeader.substring(7); + } else { + throw new IllegalArgumentException("Authorization header must start with 'Bearer '"); + } + } } diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java index 3f8a084..7a3d163 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java @@ -30,7 +30,7 @@ public User registUserByAccessToken(String accessToken) { public User findUserByAuthorizationHeader(String authorizationHeader) { try { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(extractToken(authorizationHeader)); return userRepository.findUserByAccountId(accountId); } catch (Exception e) { throw new RuntimeException(e); @@ -59,4 +59,12 @@ public void deleteUserByAccessToken(String accessToken) { throw new RuntimeException(e); } } + + private String extractToken(String authorizationHeader) { + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + return authorizationHeader.substring(7); + } else { + throw new IllegalArgumentException("Authorization header must start with 'Bearer '"); + } + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bbb133d..efb0718 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -54,4 +54,16 @@ logging: level: org: springframework: - security: DEBUG \ No newline at end of file + security: DEBUG + +epicgames: + registration: + client-id: ${EPIC_GAMES_CLIENT_ID} + client-secret: ${EPIC_GAMES_CLIENT_SECRET} + deployment-id: ${EPIC_GAMES_DEPLOYMENT_ID} + provider: + epicgames: + authorization-uri: https://www.epicgames.com/id/authorize + token-uri: https://api.epicgames.dev/epic/oauth/v2/token + account-uri: https://api.epicgames.dev/epic/id/v2/accounts + validate-uri: https://api.epicgames.dev/epic/oauth/v2/tokenInfo \ No newline at end of file From 70602bd4ecf3550365e67a4c1a0bbaaf4c8b750c Mon Sep 17 00:00:00 2001 From: in seong Park <123macanic@naver.com> Date: Wed, 16 Oct 2024 13:47:34 +0900 Subject: [PATCH 10/10] =?UTF-8?q?Test:=20Channel=20CRUD=20TestCode=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../channel/controller/ChannelController.java | 8 +- .../channel/entity/Channel.java | 5 +- .../channel/service/ChannelService.java | 7 +- .../comment/controller/CommentController.java | 6 +- .../config/JwtAuthenticationFilter.java | 4 +- .../mtvs/devlinkbackend/config/JwtUtil.java | 30 +++++--- .../ether/controller/EtherController.java | 4 +- .../guild/controller/GuildController.java | 12 +-- .../controller/Oauth2UserController.java | 19 ++++- .../oauth2/service/EpicGamesTokenService.java | 16 +--- .../oauth2/service/UserService.java | 8 +- .../controller/QuestionController.java | 6 +- .../reply/controller/ReplyController.java | 6 +- .../request/controller/RequestController.java | 6 +- .../team/controller/TeamController.java | 10 +-- .../devlinkbackend/crud/ChannelCRUDTest.java | 75 +++++++++++++++++++ 16 files changed, 155 insertions(+), 67 deletions(-) create mode 100644 src/test/java/com/mtvs/devlinkbackend/crud/ChannelCRUDTest.java diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/controller/ChannelController.java b/src/main/java/com/mtvs/devlinkbackend/channel/controller/ChannelController.java index 6c68244..0ee66b2 100644 --- a/src/main/java/com/mtvs/devlinkbackend/channel/controller/ChannelController.java +++ b/src/main/java/com/mtvs/devlinkbackend/channel/controller/ChannelController.java @@ -27,7 +27,7 @@ public ResponseEntity createChannel( @RequestBody ChannelRegistRequestDTO channelRegistRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); Channel savedChannel = channelService.saveChannel(channelRegistRequestDTO, accountId); return ResponseEntity.ok(savedChannel); } @@ -35,14 +35,14 @@ public ResponseEntity createChannel( // 모든 채널 조회 @GetMapping public ResponseEntity> getAllChannels() { - List channels = channelService.getAllChannels(); + List channels = channelService.findAllChannels(); return ResponseEntity.ok(channels); } // 특정 채널 조회 @GetMapping("/{channelId}") public ResponseEntity getChannelById(@PathVariable String channelId) { - return channelService.getChannelById(channelId) + return channelService.findChannelByChannelId(channelId) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } @@ -53,7 +53,7 @@ public ResponseEntity updateChannel( @RequestBody ChannelUpdateRequestDTO channelUpdateRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); try { Channel updatedChannel = channelService.updateChannel(channelUpdateRequestDTO, accountId); return ResponseEntity.ok(updatedChannel); diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java b/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java index 1f0c9f2..f3c08a0 100644 --- a/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java +++ b/src/main/java/com/mtvs/devlinkbackend/channel/entity/Channel.java @@ -1,11 +1,12 @@ package com.mtvs.devlinkbackend.channel.entity; -import jakarta.persistence.*; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.mongodb.core.annotation.Collation; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; @@ -16,7 +17,7 @@ @Data @NoArgsConstructor @ToString -@Document(collation = "channel") +@Document(collection = "channel") public class Channel { @Id private String channelId; diff --git a/src/main/java/com/mtvs/devlinkbackend/channel/service/ChannelService.java b/src/main/java/com/mtvs/devlinkbackend/channel/service/ChannelService.java index 1b5f65a..646dea2 100644 --- a/src/main/java/com/mtvs/devlinkbackend/channel/service/ChannelService.java +++ b/src/main/java/com/mtvs/devlinkbackend/channel/service/ChannelService.java @@ -5,6 +5,7 @@ import com.mtvs.devlinkbackend.channel.entity.Channel; import com.mtvs.devlinkbackend.channel.repository.ChannelRepository; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @@ -18,6 +19,7 @@ public ChannelService(ChannelRepository channelRepository) { } // 새 채널 저장 + @Transactional public Channel saveChannel(ChannelRegistRequestDTO channelRegistRequestDTO, String accountId) { return channelRepository.save(new Channel( accountId, @@ -27,16 +29,17 @@ public Channel saveChannel(ChannelRegistRequestDTO channelRegistRequestDTO, Stri // 모든 채널 조회 - public List getAllChannels() { + public List findAllChannels() { return channelRepository.findAll(); } // ID로 특정 채널 조회 - public Optional getChannelById(String channelId) { + public Optional findChannelByChannelId(String channelId) { return channelRepository.findById(channelId); } // ID로 채널 업데이트 + @Transactional public Channel updateChannel(ChannelUpdateRequestDTO channelUpdateRequestDTO, String accountId) { Optional channel = channelRepository.findById(channelUpdateRequestDTO.getChannelId()); if (channel.isPresent()) { diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java b/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java index 863c2f2..5204461 100644 --- a/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java +++ b/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java @@ -35,7 +35,7 @@ public ResponseEntity registComment( @RequestBody CommentRegistRequestDTO commentRegistRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); Comment comment = commentService.registComment(commentRegistRequestDTO, accountId); return ResponseEntity.status(HttpStatus.CREATED).body(comment); } @@ -65,7 +65,7 @@ public ResponseEntity> findCommentsByRequestId(@PathVariable Long public ResponseEntity> findCommentsByAccountId( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); List comments = commentService.findCommentsByAccountId(accountId); return ResponseEntity.ok(comments); } @@ -81,7 +81,7 @@ public ResponseEntity updateComment( @RequestBody CommentUpdateRequestDTO commentUpdateRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); Comment updatedComment = commentService.updateComment(commentUpdateRequestDTO, accountId); return ResponseEntity.ok(updatedComment); } diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java index e3818d6..b465602 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java @@ -1,7 +1,5 @@ package com.mtvs.devlinkbackend.config; -import com.mtvs.devlinkbackend.oauth2.entity.User; -import com.mtvs.devlinkbackend.oauth2.service.EpicGamesTokenService; import com.mtvs.devlinkbackend.oauth2.service.UserService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -46,7 +44,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse try { // 토큰 검증 | 검증 성공 시 SecurityContext에 인증 정보 저장 - String accountId = jwtUtil.getSubjectFromTokenWithAuth(token); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithAuth(token); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(accountId, null, null); diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtUtil.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtUtil.java index 10cdfbd..0947631 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtUtil.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtUtil.java @@ -5,7 +5,6 @@ import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.JWK; -import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.proc.BadJWTException; @@ -29,33 +28,33 @@ public JwtUtil(EpicGamesJWKCache jwkCache) { } // JWT 서명 및 검증을 통한 Claims 추출 - public Map getClaimsFromTokenWithAuth(String token) throws Exception { + public Map getClaimsFromAuthHeaderWithAuth(String authorizationHeader) throws Exception { // Claims 검증 - JWTClaimsSet claims = getClaimsFromToken(token); + JWTClaimsSet claims = getClaimsFromToken(extractToken(authorizationHeader)); validateClaims(claims); // 검증이 완료되었을 경우 모든 Claims을 Map으로 변환하여 반환 return convertClaimsToMap(claims); } - // JWT 서명 및 검증을 통한 Claims 추출 - public Map getClaimsFromTokenWithoutAuth(String token) throws Exception { + // JWT Claims 추출 + public Map getClaimsFromAuthHeaderWithoutAuth(String authorizationHeader) throws Exception { // Claims 검증 - JWTClaimsSet claims = getClaimsFromToken(token); + JWTClaimsSet claims = getClaimsFromToken(extractToken(authorizationHeader)); // 검증이 완료되었을 경우 모든 Claims을 Map으로 변환하여 반환 return convertClaimsToMap(claims); } // 검증된 'sub' 값, accountId 반환 - public String getSubjectFromTokenWithAuth(String token) throws Exception { - Map claims = getClaimsFromTokenWithAuth(token); + public String getSubjectFromAuthHeaderWithAuth(String authorizationHeader) throws Exception { + Map claims = getClaimsFromAuthHeaderWithAuth(authorizationHeader); return (String) claims.get("sub"); } - // 검증된 'sub' 값, accountId 반환 - public String getSubjectFromTokenWithoutAuth(String token) throws Exception { - Map claims = getClaimsFromTokenWithoutAuth(token); + // 검증되지 않은 'sub' 값, accountId 반환 + public String getSubjectFromAuthHeaderWithoutAuth(String authorizationHeader) throws Exception { + Map claims = getClaimsFromAuthHeaderWithoutAuth(authorizationHeader); return (String) claims.get("sub"); } @@ -105,4 +104,13 @@ private Map convertClaimsToMap(JWTClaimsSet claims) { claims.getClaims().forEach(claimsMap::put); return claimsMap; } + + private String extractToken(String authorizationHeader) { + System.out.println(authorizationHeader); + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + return authorizationHeader.substring(7); + } else { + throw new IllegalArgumentException("Authorization header must start with 'Bearer '"); + } + } } 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 52009bd..864aa99 100644 --- a/src/main/java/com/mtvs/devlinkbackend/ether/controller/EtherController.java +++ b/src/main/java/com/mtvs/devlinkbackend/ether/controller/EtherController.java @@ -35,7 +35,7 @@ public ResponseEntity registEther( @RequestBody EtherRegistRequestDTO etherRegistRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); Ether newEther = etherService.registEther(etherRegistRequestDTO, accountId); return ResponseEntity.ok(newEther); } @@ -61,7 +61,7 @@ public ResponseEntity findEtherByEtherId(@PathVariable Long etherId) { public ResponseEntity> findEthersByAccountId( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); List ethers = etherService.findEthersByAccountId(accountId); return ResponseEntity.ok(ethers); } diff --git a/src/main/java/com/mtvs/devlinkbackend/guild/controller/GuildController.java b/src/main/java/com/mtvs/devlinkbackend/guild/controller/GuildController.java index b0a4c0e..f20ba5e 100644 --- a/src/main/java/com/mtvs/devlinkbackend/guild/controller/GuildController.java +++ b/src/main/java/com/mtvs/devlinkbackend/guild/controller/GuildController.java @@ -35,7 +35,7 @@ public Guild createGuild( @RequestBody GuildRegistRequestDTO guildRegistRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); return guildService.createGuild(guildRegistRequestDTO, accountId); } @@ -62,7 +62,7 @@ public List findGuildsByGuildNameContaining(@RequestParam String guildNam public List findGuildsByOwnerId( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String ownerId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String ownerId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); return guildService.findGuildsByOwnerId(ownerId); } @@ -72,7 +72,7 @@ public List findGuildsByOwnerId( public List findGuildsByMemberIdContaining( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String memberId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String memberId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); return guildService.findGuildsByMemberIdContaining(memberId); } @@ -87,7 +87,7 @@ public Guild updateGuild( @RequestBody GuildUpdateRequestDTO guildUpdateRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); return guildService.updateGuild(guildUpdateRequestDTO, accountId); } @@ -102,7 +102,7 @@ public Guild addMemberToGuild( @RequestBody GuildMemberModifyRequestDTO guildMemberModifyRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); return guildService.addMemberToGuild(guildMemberModifyRequestDTO, accountId); } @@ -117,7 +117,7 @@ public Guild removeMemberFromGuild( @RequestBody GuildMemberModifyRequestDTO guildMemberModifyRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); return guildService.removeMemberToGuild(guildMemberModifyRequestDTO, accountId); } 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 1e2ff58..69be88c 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.config.JwtUtil; import com.mtvs.devlinkbackend.oauth2.dto.EpicGamesCallbackRequestDTO; import com.mtvs.devlinkbackend.oauth2.entity.User; import com.mtvs.devlinkbackend.oauth2.service.EpicGamesTokenService; @@ -7,8 +8,6 @@ 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.*; import org.springframework.web.bind.annotation.*; @@ -21,10 +20,12 @@ public class Oauth2UserController { private final EpicGamesTokenService epicGamesTokenService; private final UserService userService; + private final JwtUtil jwtUtil; - public Oauth2UserController(EpicGamesTokenService epicGamesTokenService, UserService userService) { + public Oauth2UserController(EpicGamesTokenService epicGamesTokenService, UserService userService, JwtUtil jwtUtil) { this.epicGamesTokenService = epicGamesTokenService; this.userService = userService; + this.jwtUtil = jwtUtil; } // 로컬 user 정보 가져오는 API @@ -39,6 +40,7 @@ public Oauth2UserController(EpicGamesTokenService epicGamesTokenService, UserSer @ApiResponse(responseCode = "401", description = "인증되지 않음") }) public ResponseEntity getLocalUserInfo(@RequestHeader("Authorization") String authorizationHeader) { + try { return ResponseEntity.ok(userService.findUserByAuthorizationHeader(authorizationHeader)); } catch (Exception e) { @@ -72,6 +74,17 @@ public ResponseEntity getEpicGamesUserInfo( } } + @GetMapping("/epicgames/accountId") + public ResponseEntity getAccountId( + @RequestHeader("Authorization") String authorizationHeader) { + + try { + return ResponseEntity.ok(jwtUtil.getSubjectFromAuthHeaderWithAuth(authorizationHeader)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token"); + } + } + @PostMapping("/epicgames/callback") @Operation( summary = "EpicGames AccessToken 요청", diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/EpicGamesTokenService.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/EpicGamesTokenService.java index 85f7f40..64c6cd8 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/EpicGamesTokenService.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/EpicGamesTokenService.java @@ -28,7 +28,7 @@ public class EpicGamesTokenService { private final String getAuthorizationCodeURL = "https://www.epicgames.com/id/authorize"; private final String getAccessTokenURL = "https://api.epicgames.dev/epic/oauth/v2/token"; - private final String getAccountURL = "https://api.epicgames.dev/epic/id/v2/accounts"; + private final String getAccountURL = "https://api.epicgames.dev/epic/id/v2/accounts?accountId="; private final EpicGamesJWKCache jwkCache; private final JwtUtil jwtUtil; @@ -40,9 +40,9 @@ public EpicGamesTokenService(EpicGamesJWKCache jwkCache, JwtUtil jwtUtil) { } // 오프라인 JWT 검증 및 파싱 메서드 - public Map validateAndParseToken(String token) throws Exception { + public Map validateAuthHeaderAndParseToken(String authorizationHeader) throws Exception { // JWT 토큰 검증 및 파싱하여 Claims를 추출 - return jwtUtil.getClaimsFromTokenWithoutAuth(token); + return jwtUtil.getClaimsFromAuthHeaderWithAuth(authorizationHeader); } public Map getAccessTokenAndRefreshTokenByCode(String code) { @@ -91,7 +91,7 @@ public List> getEpicGamesUserAccount(String authorizationHea // Epic Games 토큰 엔드포인트 요청 ResponseEntity>> response; - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(extractToken(authorizationHeader)); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); System.out.println(accountId); try { response = restTemplate.exchange( @@ -108,12 +108,4 @@ public List> getEpicGamesUserAccount(String authorizationHea return response.getBody(); } - - private String extractToken(String authorizationHeader) { - if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { - return authorizationHeader.substring(7); - } else { - throw new IllegalArgumentException("Authorization header must start with 'Bearer '"); - } - } } diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java index 7a3d163..fdb5882 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/service/UserService.java @@ -19,7 +19,7 @@ public UserService(UserRepository userRepository, JwtUtil jwtUtil) { public User registUserByAccessToken(String accessToken) { try { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(accessToken); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(accessToken); return userRepository.save(new User( accountId )); @@ -30,7 +30,7 @@ public User registUserByAccessToken(String accessToken) { public User findUserByAuthorizationHeader(String authorizationHeader) { try { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(extractToken(authorizationHeader)); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(extractToken(authorizationHeader)); return userRepository.findUserByAccountId(accountId); } catch (Exception e) { throw new RuntimeException(e); @@ -39,7 +39,7 @@ public User findUserByAuthorizationHeader(String authorizationHeader) { public void updateUserByAccessToken(String accessToken, UserUpdateRequestDTO userUpdateRequestDTO) { try { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(accessToken); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(accessToken); User user = userRepository.findUserByAccountId(accountId); if(user != null) { user.setEmail(userUpdateRequestDTO.getEmail()); @@ -53,7 +53,7 @@ public void updateUserByAccessToken(String accessToken, UserUpdateRequestDTO use public void deleteUserByAccessToken(String accessToken) { try { - String accountId = jwtUtil.getSubjectFromTokenWithAuth(accessToken); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithAuth(accessToken); userRepository.deleteUserByAccountId(accountId); } catch (Exception e) { throw new RuntimeException(e); 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 52e3427..c51567d 100644 --- a/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java +++ b/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java @@ -42,7 +42,7 @@ public ResponseEntity createQuestion( @RequestBody QuestionRegistRequestDTO questionRegistRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithAuth(authorizationHeader); Question createdQuestion = questionService.registQuestion(questionRegistRequestDTO, accountId); return ResponseEntity.ok(createdQuestion); } @@ -98,7 +98,7 @@ public ResponseEntity> getQuestionsByAccountIdWithPaging( @RequestParam int page, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); List questions = questionService.findQuestionsByAccountIdWithPaging(page, accountId); return ResponseEntity.ok(questions); } @@ -118,7 +118,7 @@ public ResponseEntity updateQuestion( @RequestBody QuestionUpdateRequestDTO questionUpdateRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); try { Question updatedQuestion = questionService.updateQuestion(questionUpdateRequestDTO, accountId); return ResponseEntity.ok(updatedQuestion); 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 ef2014a..e010ac2 100644 --- a/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java +++ b/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java @@ -36,7 +36,7 @@ public ResponseEntity registReply( @RequestBody ReplyRegistRequestDTO replyRegistRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithAuth(authorizationHeader); Reply reply = replyService.registReply(replyRegistRequestDTO, accountId); return ResponseEntity.ok(reply); } @@ -75,7 +75,7 @@ public ResponseEntity> findRepliesByQuestionId(@PathVariable Long qu public ResponseEntity> findRepliesByAccountId( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); List replies = replyService.findRepliesByAccountId(accountId); return ResponseEntity.ok(replies); } @@ -91,7 +91,7 @@ public ResponseEntity updateReply( @RequestBody ReplyUpdateRequestDTO replyUpdateRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); try { Reply updatedReply = replyService.updateReply(replyUpdateRequestDTO, accountId); return ResponseEntity.ok(updatedReply); diff --git a/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java b/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java index adae769..76f7f67 100644 --- a/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java +++ b/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java @@ -37,7 +37,7 @@ public ResponseEntity registerRequest( @RequestBody RequestRegistRequestDTO requestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); Request newRequest = requestService.registRequest(requestDTO, accountId); return new ResponseEntity<>(newRequest, HttpStatus.CREATED); } @@ -65,7 +65,7 @@ public ResponseEntity getRequestById(@PathVariable Long requestId) { public ResponseEntity> getRequestsByAccountId( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); List requests = requestService.findRequestsByAccountId(accountId); return ResponseEntity.ok(requests); } @@ -94,7 +94,7 @@ public ResponseEntity updateRequest( @RequestHeader(name = "Authorization") String authorizationHeader) { try { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); Request updatedRequest = requestService.updateRequest(requestDTO, accountId); return ResponseEntity.ok(updatedRequest); } catch (Exception e) { diff --git a/src/main/java/com/mtvs/devlinkbackend/team/controller/TeamController.java b/src/main/java/com/mtvs/devlinkbackend/team/controller/TeamController.java index 4153b07..4ed5681 100644 --- a/src/main/java/com/mtvs/devlinkbackend/team/controller/TeamController.java +++ b/src/main/java/com/mtvs/devlinkbackend/team/controller/TeamController.java @@ -1,8 +1,6 @@ package com.mtvs.devlinkbackend.team.controller; import com.mtvs.devlinkbackend.config.JwtUtil; -import com.mtvs.devlinkbackend.guild.dto.GuildMemberModifyRequestDTO; -import com.mtvs.devlinkbackend.guild.entity.Guild; import com.mtvs.devlinkbackend.team.dto.TeamMemberModifyRequestDTO; import com.mtvs.devlinkbackend.team.dto.TeamRegistRequestDTO; import com.mtvs.devlinkbackend.team.dto.TeamUpdateRequestDTO; @@ -66,7 +64,7 @@ public ResponseEntity getTeamById( public ResponseEntity> getTeamsByPmId( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String pmId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String pmId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); List teams = teamService.findTeamsByPmId(pmId); return ResponseEntity.ok(teams); } @@ -91,7 +89,7 @@ public ResponseEntity> getTeamsByTeamNameContaining( public ResponseEntity> getTeamsByMemberIdContaining( @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String memberId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String memberId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); List teams = teamService.findTeamsByMemberIdContaining(memberId); return ResponseEntity.ok(teams); } @@ -126,7 +124,7 @@ public Team addMemberToTeam( @RequestBody TeamMemberModifyRequestDTO teamMemberModifyRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); return teamService.addMemberToTeam(teamMemberModifyRequestDTO, accountId); } @@ -141,7 +139,7 @@ public Team removeMemberFromTeam( @RequestBody TeamMemberModifyRequestDTO teamMemberModifyRequestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + String accountId = jwtUtil.getSubjectFromAuthHeaderWithoutAuth(authorizationHeader); return teamService.removeMemberToTeam(teamMemberModifyRequestDTO, accountId); } diff --git a/src/test/java/com/mtvs/devlinkbackend/crud/ChannelCRUDTest.java b/src/test/java/com/mtvs/devlinkbackend/crud/ChannelCRUDTest.java new file mode 100644 index 0000000..cdc8514 --- /dev/null +++ b/src/test/java/com/mtvs/devlinkbackend/crud/ChannelCRUDTest.java @@ -0,0 +1,75 @@ +package com.mtvs.devlinkbackend.crud; + +import com.mtvs.devlinkbackend.channel.dto.ChannelRegistRequestDTO; +import com.mtvs.devlinkbackend.channel.dto.ChannelUpdateRequestDTO; +import com.mtvs.devlinkbackend.channel.entity.PositionType; +import com.mtvs.devlinkbackend.channel.service.ChannelService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.stream.Stream; + +@SpringBootTest +@Transactional +public class ChannelCRUDTest { + + @Autowired + private ChannelService channelService; + + private static Stream newChannel() { + return Stream.of( + Arguments.of(new ChannelRegistRequestDTO(Arrays.asList(new PositionType(new PositionType.Position(1, 2, 3), "chair"))), "0bb31d25962c4817be894044371d4d3c"), + Arguments.of(new ChannelRegistRequestDTO(Arrays.asList(new PositionType(new PositionType.Position(4, 5, 6), "grass"))), "0bb31d25962c4817be894044371d4d3c") + ); + } + + private static Stream modifiedChannel() { + return Stream.of( + Arguments.of(new ChannelUpdateRequestDTO("27ceb93e-bf35-4058-9a07-46550cf1aa00", Arrays.asList(new PositionType(new PositionType.Position(1, 2, 3), "chair"))), "0bb31d25962c4817be894044371d4d3c"), + Arguments.of(new ChannelUpdateRequestDTO("27ceb93e-bf35-4058-9a07-46550cf1aa00", Arrays.asList(new PositionType(new PositionType.Position(3, 1, 2), "block"))), "0bb31d25962c4817be894044371d4d3c") + ); + } + + @DisplayName("채널 등록 테스트") + @ParameterizedTest + @MethodSource("newChannel") + public void testCreateChannel(ChannelRegistRequestDTO channelRegistRequestDTO, String accountId) { + Assertions.assertDoesNotThrow(() -> channelService.saveChannel(channelRegistRequestDTO, accountId)); + } + + @DisplayName("채널 PK로 조회 테스트") + @ValueSource(strings = {"79673000-2800-4226-ac32-fae65cd0a55c"}) + @ParameterizedTest + public void testFindChannelByChannelId(String channelId) { + Assertions.assertDoesNotThrow(() -> channelService.findChannelByChannelId(channelId)); + } + + @DisplayName("모든 채널 조회 테스트") + @Test + public void testFindAllChannels() { + Assertions.assertDoesNotThrow(() -> channelService.findAllChannels()); + } + + @DisplayName("채널 수정 테스트") + @MethodSource("modifiedChannel") + @ParameterizedTest + public void testUpdateChannel(ChannelUpdateRequestDTO channelUpdateRequestDTO, String accountId) { + Assertions.assertDoesNotThrow(() -> channelService.updateChannel(channelUpdateRequestDTO, accountId)); + } + + @DisplayName("채널 삭제 테스트") + @ValueSource(strings = {"79673000-2800-4226-ac32-fae65cd0a55c"}) + @ParameterizedTest + public void testDeleteChannel(String channelId) { + Assertions.assertDoesNotThrow(() -> channelService.deleteChannel(channelId)); + } +}