diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..9c9a32a Binary files /dev/null and b/.DS_Store differ diff --git a/.gradle/8.8/checksums/checksums.lock b/.gradle/8.8/checksums/checksums.lock new file mode 100644 index 0000000..0a0d160 Binary files /dev/null and b/.gradle/8.8/checksums/checksums.lock differ diff --git a/.gradle/8.8/dependencies-accessors/gc.properties b/.gradle/8.8/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/8.8/fileChanges/last-build.bin b/.gradle/8.8/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/.gradle/8.8/fileChanges/last-build.bin differ diff --git a/.gradle/8.8/fileHashes/fileHashes.lock b/.gradle/8.8/fileHashes/fileHashes.lock new file mode 100644 index 0000000..b8074bc Binary files /dev/null and b/.gradle/8.8/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.8/gc.properties b/.gradle/8.8/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..a8d2a19 Binary files /dev/null and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..1b1c8a0 --- /dev/null +++ b/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Mon Sep 02 17:21:05 KST 2024 +gradle.version=8.8 diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.idea/modules.xml b/.idea/modules.xml index 74f1bcb..1fb2494 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/.idea/modules/backend.main.iml b/.idea/modules/backend.main.iml new file mode 100644 index 0000000..0676e50 --- /dev/null +++ b/.idea/modules/backend.main.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/backend/.DS_Store b/backend/.DS_Store new file mode 100644 index 0000000..6ef277f Binary files /dev/null and b/backend/.DS_Store differ diff --git a/backend/.gitignore b/backend/.gitignore index c2065bc..ec18673 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -19,6 +19,7 @@ bin/ ### IntelliJ IDEA ### .idea +.idea/ *.iws *.iml *.ipr diff --git a/backend/build.gradle b/backend/build.gradle index cb425ca..95235d4 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -24,6 +24,7 @@ ext { dependencies { // Spring Boot starters + implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' diff --git a/backend/src/.DS_Store b/backend/src/.DS_Store new file mode 100644 index 0000000..bc65ca7 Binary files /dev/null and b/backend/src/.DS_Store differ diff --git a/backend/src/main/.DS_Store b/backend/src/main/.DS_Store new file mode 100644 index 0000000..c387e22 Binary files /dev/null and b/backend/src/main/.DS_Store differ diff --git a/backend/src/main/java/.DS_Store b/backend/src/main/java/.DS_Store new file mode 100644 index 0000000..a832bac Binary files /dev/null and b/backend/src/main/java/.DS_Store differ diff --git a/backend/src/main/java/com/.DS_Store b/backend/src/main/java/com/.DS_Store new file mode 100644 index 0000000..282a4c6 Binary files /dev/null and b/backend/src/main/java/com/.DS_Store differ diff --git a/backend/src/main/java/com/metlab_project/.DS_Store b/backend/src/main/java/com/metlab_project/.DS_Store index 8c3a2d3..98c1b2b 100644 Binary files a/backend/src/main/java/com/metlab_project/.DS_Store and b/backend/src/main/java/com/metlab_project/.DS_Store differ diff --git a/backend/src/main/java/com/metlab_project/backend/.DS_Store b/backend/src/main/java/com/metlab_project/backend/.DS_Store index 2cb0fe6..83a98a4 100644 Binary files a/backend/src/main/java/com/metlab_project/backend/.DS_Store and b/backend/src/main/java/com/metlab_project/backend/.DS_Store differ diff --git a/backend/src/main/java/com/metlab_project/backend/controller/chatroom/ChatRoomController.java b/backend/src/main/java/com/metlab_project/backend/controller/chatroom/ChatRoomController.java index 42d55f1..1c38486 100644 --- a/backend/src/main/java/com/metlab_project/backend/controller/chatroom/ChatRoomController.java +++ b/backend/src/main/java/com/metlab_project/backend/controller/chatroom/ChatRoomController.java @@ -1,4 +1,5 @@ package com.metlab_project.backend.controller.chatroom; + import com.metlab_project.backend.domain.dto.chatroom.ChatroomCreateRequest; import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; @@ -8,54 +9,65 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/chatroom") -public class ChatroomController { +public class ChatRoomController { @GetMapping("/list") - @Operation(summary="WAITING 상태인 채팅룸 목록 불러오기", description="WAITING 상태인 채팅룸의 목록을 불러옵니다.") + @Operation(summary = "WAITING 상태인 채팅룸 목록 불러오기", description = "WAITING 상태인 채팅룸의 목록을 불러옵니다.") public ResponseEntity getAllChatroomList() { try { - - } catch () { + // TODO: 채팅룸 목록을 불러오는 로직을 구현하세요. + } catch (Exception e) { + // TODO: 예외 처리 로직을 구현하세요. + return ResponseEntity.status(500).body("서버 오류가 발생했습니다."); } + return ResponseEntity.ok("채팅룸 목록"); // TODO: 실제 데이터를 반환하도록 수정하세요. } @GetMapping("/list/{schoolEmail}") - @Operation(summary="유저가 참여하고 있는 채팅룸 목록 불러오기", description="유저가 참여하고 있는 채팅룸의 목록을 불러옵니다.") - public ResponseEntity getChatroomList(@PathVariable String schoolEmail){ - try{ - - }catch(){ - + @Operation(summary = "유저가 참여하고 있는 채팅룸 목록 불러오기", description = "유저가 참여하고 있는 채팅룸의 목록을 불러옵니다.") + public ResponseEntity getChatroomList(@PathVariable String schoolEmail) { + try { + // TODO: 유저가 참여하고 있는 채팅룸 목록을 불러오는 로직을 구현하세요. + } catch (Exception e) { + // TODO: 예외 처리 로직을 구현하세요. + return ResponseEntity.status(500).body("서버 오류가 발생했습니다."); } + return ResponseEntity.ok("참여 중인 채팅룸 목록"); // TODO: 실제 데이터를 반환하도록 수정하세요. } @GetMapping("/{chatroomid}/participant") - @Operation(summary="특정 채팅룸의 참여자 목록 불러오기", description="특정 채팅룸의 참여자 목록을 불러옵니다.") - public ResponseEntity getChatroomParticipant(@PathVariable("chatroomid") String chatroomId){ - try{ - - }catch(){ - + @Operation(summary = "특정 채팅룸의 참여자 목록 불러오기", description = "특정 채팅룸의 참여자 목록을 불러옵니다.") + public ResponseEntity getChatroomParticipant(@PathVariable("chatroomid") String chatroomId) { + try { + // TODO: 특정 채팅룸의 참여자 목록을 불러오는 로직을 구현하세요. + } catch (Exception e) { + // TODO: 예외 처리 로직을 구현하세요. + return ResponseEntity.status(500).body("서버 오류가 발생했습니다."); } + return ResponseEntity.ok("채팅룸 참여자 목록"); // TODO: 실제 데이터를 반환하도록 수정하세요. } @PostMapping - @Operation(summary="채팅룸 생성하기", description="채팅룸을 생성합니다.") - public ResponseEntity createChatroom(@RequestBody ChatroomCreateRequest request){ - try{ - - }catch(){ - + @Operation(summary = "채팅룸 생성하기", description = "채팅룸을 생성합니다.") + public ResponseEntity createChatroom(@RequestBody ChatroomCreateRequest request) { + try { + // TODO: 채팅룸을 생성하는 로직을 구현하세요. + } catch (Exception e) { + // TODO: 예외 처리 로직을 구현하세요. + return ResponseEntity.status(500).body("서버 오류가 발생했습니다."); } + return ResponseEntity.ok("채팅룸 생성 완료"); // TODO: 실제 데이터를 반환하도록 수정하세요. } @PostMapping("/activate") - @Operation(summary="채팅룸 활성화 하기", description="채팅룸의 상태를 active로 전환합니다.") - public ResponseEntity activeChatroom(){ - try{ - - }catch(){ - + @Operation(summary = "채팅룸 활성화 하기", description = "채팅룸의 상태를 active로 전환합니다.") + public ResponseEntity activeChatroom() { + try { + // TODO: 채팅룸을 활성화하는 로직을 구현하세요. + } catch (Exception e) { + // TODO: 예외 처리 로직을 구현하세요. + return ResponseEntity.status(500).body("서버 오류가 발생했습니다."); } + return ResponseEntity.ok("채팅룸 활성화 완료"); // TODO: 실제 데이터를 반환하도록 수정하세요. } } diff --git a/backend/src/main/java/com/metlab_project/backend/controller/user/UserController.java b/backend/src/main/java/com/metlab_project/backend/controller/user/UserController.java index 1f22f3b..27621ce 100644 --- a/backend/src/main/java/com/metlab_project/backend/controller/user/UserController.java +++ b/backend/src/main/java/com/metlab_project/backend/controller/user/UserController.java @@ -1,7 +1,44 @@ package com.metlab_project.backend.controller.user; -import org.springframework.web.bind.annotation.RestController; +import com.metlab_project.backend.domain.dto.user.UserInfoResponse; +import com.metlab_project.backend.service.user.UserService; +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RequiredArgsConstructor @RestController +@RequestMapping("/user") public class UserController { -} + + private final UserService userService; + + // 유저 정보 가져옴 + @GetMapping("/{schoolEmail}") + public ResponseEntity getUserInfo(@PathVariable String schoolEmail) { + try { + UserInfoResponse userInfo = userService.getUserInfoBySchoolEmail(schoolEmail); + return new ResponseEntity<>(userInfo, HttpStatus.OK); + } catch (EntityNotFoundException ex) { + return new ResponseEntity<>("User not found", HttpStatus.NOT_FOUND); + } + } + + // 유저 정보 수정 + @PutMapping("/{schoolEmail}") + public ResponseEntity updateUserInfo(@PathVariable String schoolEmail, @RequestBody UserInfoResponse updatedUserInfo) { + try { + UserInfoResponse updatedUser = userService.updateUserInfo(schoolEmail, updatedUserInfo); + return new ResponseEntity<>(updatedUser, HttpStatus.OK); + } catch (EntityNotFoundException ex) { + return new ResponseEntity<>("User not found", HttpStatus.NOT_FOUND); + } catch (IllegalArgumentException ex) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/CustomUserDetails.java b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/CustomUserDetails.java new file mode 100644 index 0000000..b259b5c --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/CustomUserDetails.java @@ -0,0 +1,55 @@ +package com.metlab_project.backend.domain.dto.user; + +import com.metlab_project.backend.domain.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Collection; + +@RequiredArgsConstructor +public class CustomUserDetails implements UserDetails { + private final User user; + + public Collection getAuthorities(){ + Collection collection = new ArrayList<>(); + collection.add((GrantedAuthority) () -> user.getRole().toString()); + + return collection; + } + + public User getUser(){ + return this.user; + } + + @Override + public String getUsername(){ + return user.getSchoolEmail(); + } + + @Override + public String getPassword(){ + return user.getPassword(); + } + + @Override + public boolean isAccountNonExpired(){ + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoRequest.java b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoRequest.java new file mode 100644 index 0000000..cc2000a --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoRequest.java @@ -0,0 +1,22 @@ +package com.metlab_project.backend.domain.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserInfoRequest { + private String schoolEmail; + private String password; + private String nickname; + private String birthday; + private String gender; + + private String studentId; + private String college; + private String department; +} \ No newline at end of file diff --git a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse.java b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse.java index d10ef8f..20c5fbb 100644 --- a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse.java +++ b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse.java @@ -1,20 +1,33 @@ package com.metlab_project.backend.domain.dto.user; -import lombok.AllArgsConstructor; +import com.metlab_project.backend.domain.entity.User; + import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.Getter; +import lombok.Setter; + -@Data +@Getter +@Setter @Builder -@NoArgsConstructor -@AllArgsConstructor public class UserInfoResponse { private String schoolEmail; private String nickname; private String gender; - private String studentId; private String college; private String department; + private UserRole role; + + public static UserInfoResponse from(User user){ + return UserInfoResponse.builder() + .schoolEmail(user.getSchoolEmail()) + .nickname(user.getNickname()) + .gender(user.getGender()) + .studentId(user.getStudentId()) + .college(user.getCollege()) + .department(user.getDepartment()) + .role(user.getRole()) + .build(); + } } diff --git a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_BACKUP_42285.java b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_BACKUP_42285.java new file mode 100644 index 0000000..20c5fbb --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_BACKUP_42285.java @@ -0,0 +1,33 @@ +package com.metlab_project.backend.domain.dto.user; + +import com.metlab_project.backend.domain.entity.User; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + + +@Getter +@Setter +@Builder +public class UserInfoResponse { + private String schoolEmail; + private String nickname; + private String gender; + private String studentId; + private String college; + private String department; + private UserRole role; + + public static UserInfoResponse from(User user){ + return UserInfoResponse.builder() + .schoolEmail(user.getSchoolEmail()) + .nickname(user.getNickname()) + .gender(user.getGender()) + .studentId(user.getStudentId()) + .college(user.getCollege()) + .department(user.getDepartment()) + .role(user.getRole()) + .build(); + } +} diff --git a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_BASE_42285.java b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_BASE_42285.java new file mode 100644 index 0000000..d10ef8f --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_BASE_42285.java @@ -0,0 +1,20 @@ +package com.metlab_project.backend.domain.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserInfoResponse { + private String schoolEmail; + private String nickname; + private String gender; + + private String studentId; + private String college; + private String department; +} diff --git a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_LOCAL_42285.java b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_LOCAL_42285.java new file mode 100644 index 0000000..ea41b9a --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_LOCAL_42285.java @@ -0,0 +1,34 @@ +package com.metlab_project.backend.domain.dto.user; + +import com.metlab_project.backend.domain.entity.User; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + + +@Getter +@Setter +@Builder + +public class UserInfoResponse { + private String schoolEmail; + private String nickname; + private String gender; + private String studentId; + private String college; + private String department; + + public static UserInfoResponse from(User user){ + return UserInfoResponse.builder() + .schoolEmail(user.getSchoolEmail()) + .nickname(user.getNickname()) + .gender(user.getGender()) + .studentId(user.getStudentId()) + .college(user.getCollege()) + .department(user.getDepartment()) + .build(); + + } + +} diff --git a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_REMOTE_42285.java b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_REMOTE_42285.java new file mode 100644 index 0000000..e695811 --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserInfoResponse_REMOTE_42285.java @@ -0,0 +1,21 @@ +package com.metlab_project.backend.domain.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserInfoResponse { + private String schoolEmail; + private String nickname; + private String gender; + + private String studentId; + private String college; + private String department; + private UserRole role; +} diff --git a/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserRole.java b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserRole.java new file mode 100644 index 0000000..9738723 --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/domain/dto/user/UserRole.java @@ -0,0 +1,6 @@ +package com.metlab_project.backend.domain.dto.user; + +public enum UserRole { + ROLE_USER, + ROLE_ADMIN +} diff --git a/backend/src/main/java/com/metlab_project/backend/domain/entity/RefreshEntity.java b/backend/src/main/java/com/metlab_project/backend/domain/entity/RefreshEntity.java new file mode 100644 index 0000000..ae8c717 --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/domain/entity/RefreshEntity.java @@ -0,0 +1,31 @@ +package com.metlab_project.backend.domain.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Table(name = "refresh_tokens") // 테이블 이름을 지정할 수 있습니다. +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RefreshEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String schoolEmail; // 유저의 이메일(고유) + + @Column(nullable = false) + private String token; // Refresh 토큰 + + @Column(nullable = false) + private Long expiration; // 만료 시간 (초 단위로 저장) +} \ No newline at end of file diff --git a/backend/src/main/java/com/metlab_project/backend/domain/entity/User.java b/backend/src/main/java/com/metlab_project/backend/domain/entity/User.java index 803c30e..b627999 100644 --- a/backend/src/main/java/com/metlab_project/backend/domain/entity/User.java +++ b/backend/src/main/java/com/metlab_project/backend/domain/entity/User.java @@ -1,5 +1,6 @@ package com.metlab_project.backend.domain.entity; +import com.metlab_project.backend.domain.dto.user.UserRole; import jakarta.persistence.*; import jakarta.persistence.Table; import lombok.AllArgsConstructor; @@ -62,6 +63,8 @@ public class User { private Boolean isblocked; + private UserRole role; + @ManyToOne @JoinColumn(name = "chatroom_id", insertable = false, updatable = false) private ChatRoom chatRoom; diff --git a/backend/src/main/java/com/metlab_project/backend/repository/user/RefreshTokenRepository.java b/backend/src/main/java/com/metlab_project/backend/repository/user/RefreshTokenRepository.java new file mode 100644 index 0000000..2936aa2 --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/repository/user/RefreshTokenRepository.java @@ -0,0 +1,14 @@ +package com.metlab_project.backend.repository.user; + +import com.metlab_project.backend.domain.entity.RefreshEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface RefreshTokenRepository extends JpaRepository { + Optional findBySchoolEmail(String schoolEmail); // schoolEmail로 조회 + Boolean existsByToken(String token); + Optional findByToken(String token); +} diff --git a/backend/src/main/java/com/metlab_project/backend/security/SecurityConfig.java b/backend/src/main/java/com/metlab_project/backend/security/SecurityConfig.java index a9aedfa..e902a04 100644 --- a/backend/src/main/java/com/metlab_project/backend/security/SecurityConfig.java +++ b/backend/src/main/java/com/metlab_project/backend/security/SecurityConfig.java @@ -1,6 +1,6 @@ package com.metlab_project.backend.security; -import com.metlab_project.backend.security.jwt.JwtTokenFilter; +import com.metlab_project.backend.security.jwt.JwtAuthenticationFilter; import com.metlab_project.backend.security.jwt.JwtTokenProvider; import com.metlab_project.backend.security.jwt.JwtTokenValidator; import com.metlab_project.backend.service.jwt.BlacklistTokenService; @@ -59,7 +59,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{ .requestMatchers(whiteListUrl.toArray(new String[0])).permitAll() //.anyRequest().authenticated() ) - .addFilterBefore(new JwtTokenFilter(userService, jwtTokenProvider,jwtTokenValidator,blacklistTokenService), UsernamePasswordAuthenticationFilter.class); + .addFilterBefore(new JwtAuthenticationFilter(userService, jwtTokenProvider,jwtTokenValidator,blacklistTokenService), UsernamePasswordAuthenticationFilter.class); return http.build(); } diff --git a/backend/src/main/java/com/metlab_project/backend/security/jwt/CustomLoginFilter.java b/backend/src/main/java/com/metlab_project/backend/security/jwt/CustomLoginFilter.java new file mode 100644 index 0000000..b3d60b2 --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/security/jwt/CustomLoginFilter.java @@ -0,0 +1,130 @@ +package com.metlab_project.backend.security.jwt; + +import com.metlab_project.backend.repository.user.RefreshTokenRepository; +import com.metlab_project.backend.service.user.UserService; + +import io.jsonwebtoken.io.IOException; + +import com.metlab_project.backend.domain.entity.RefreshEntity; +import com.metlab_project.backend.domain.dto.user.UserInfoResponse; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import java.util.Collection; +import java.util.Iterator; + +@Slf4j +public class CustomLoginFilter extends UsernamePasswordAuthenticationFilter { + + private final RefreshTokenRepository refreshTokenRepository; + private final AuthenticationManager authenticationManager; + private final JwtTokenProvider jwtTokenProvider; // JwtTokenProvider 사용 + private final UserService userService; // UserService 사용 + + public CustomLoginFilter(AuthenticationManager authenticationManager, + RefreshTokenRepository refreshTokenRepository, + JwtTokenProvider jwtTokenProvider, + UserService userService) { + this.refreshTokenRepository = refreshTokenRepository; + this.authenticationManager = authenticationManager; + this.jwtTokenProvider = jwtTokenProvider; + this.userService = userService; + setFilterProcessesUrl("/api/users/login"); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { + + // 클라이언트 요청에서 username(schoolEmail), password 추출 + final String schoolEmail = obtainUsername(request); // schoolEmail로 변경 + final String password = obtainPassword(request); + + log.info("[attemptAuthentication] schoolEmail = {}", schoolEmail); + log.info("[attemptAuthentication] password = {}", password); + + // 스프링 시큐리티에서 username과 password를 검증하기 위해서는 token에 담아야 함 + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(schoolEmail, password, null); + + // token에 담은 검증을 위한 AuthenticationManager로 전달 + return authenticationManager.authenticate(authToken); + } + + // 로그인 성공시 실행하는 메소드 (여기서 JWT를 발급하면 됨) + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) { + log.info("[successfulAuthentication] 로그인 성공"); + + Collection authorities = authentication.getAuthorities(); + Iterator iterator = authorities.iterator(); + GrantedAuthority auth = iterator.next(); + + final String schoolEmail = authentication.getName(); + final String role = auth.getAuthority(); + + // UserService를 통해 유저 정보 가져오기 + UserInfoResponse userInfo = userService.getUserInfoBySchoolEmail(schoolEmail); + + // JwtTokenProvider를 사용하여 Access/Refresh 토큰 발급 + final String accessToken = jwtTokenProvider.generateAccessToken( + userInfo.getSchoolEmail(), + userInfo.getNickname(), + userInfo.getGender(), + userInfo.getStudentId(), + userInfo.getCollege(), + userInfo.getDepartment() + ); + final String refreshToken = jwtTokenProvider.generateRefreshToken(schoolEmail); + + log.info("[successfulAuthentication] schoolEmail = {}", schoolEmail); + log.info("[successfulAuthentication] access token = {}", accessToken); + log.info("[successfulAuthentication] refresh token = {}", refreshToken); + + // Refresh 토큰 저장 + addRefreshEntity(schoolEmail, refreshToken, jwtTokenProvider.getExpirationDateFromToken(refreshToken).getTime()); + + // Authorization 헤더와 쿠키 설정 + response.addHeader("Authorization", "Bearer " + accessToken); + response.addCookie(createCookie("refresh", refreshToken, jwtTokenProvider.getExpirationDateFromToken(refreshToken).getTime())); + response.setStatus(HttpServletResponse.SC_OK); + } + + private void addRefreshEntity(String schoolEmail, String refresh, Long refreshExpireTime) { + // 기존에 존재하는 Refresh 토큰 삭제 + refreshTokenRepository.findBySchoolEmail(schoolEmail).ifPresent(refreshTokenRepository::delete); + + RefreshEntity refreshEntity = RefreshEntity.builder() + .schoolEmail(schoolEmail) + .token(refresh) + .expiration(refreshExpireTime) + .build(); + + refreshTokenRepository.save(refreshEntity); + } + + private Cookie createCookie(String key, String value, Long refreshExpireTime) { + Cookie cookie = new Cookie(key, value); + cookie.setHttpOnly(true); + cookie.setMaxAge(Math.toIntExact(refreshExpireTime / 1000)); + cookie.setSecure(true); // HTTPS에서만 전송 + cookie.setPath("/"); // 경로 지정 + return cookie; + } + + //로그인 실패시 실행하는 메소드 + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) { + log.info("[successfulAuthentication] 로그인 실패"); + response.setStatus(401); + } + +} diff --git a/backend/src/main/java/com/metlab_project/backend/security/jwt/CustomLogoutFilter.java b/backend/src/main/java/com/metlab_project/backend/security/jwt/CustomLogoutFilter.java new file mode 100644 index 0000000..ac68968 --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/security/jwt/CustomLogoutFilter.java @@ -0,0 +1,142 @@ +package com.metlab_project.backend.security.jwt; + +import com.metlab_project.backend.repository.user.RefreshTokenRepository; +import com.metlab_project.backend.domain.entity.RefreshEntity; +import com.metlab_project.backend.service.user.UserService; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.filter.GenericFilterBean; + +import java.io.IOException; + +@RequiredArgsConstructor +@Slf4j +public class CustomLogoutFilter extends GenericFilterBean { + + private final JwtTokenProvider jwtTokenProvider; // JwtTokenProvider + private final RefreshTokenRepository refreshTokenRepository; + + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); + } + + /** + * 로그아웃 필터 + * @param request + * @param response + * @param filterChain + * @throws IOException + * @throws ServletException + */ + private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { + + // Logout Request 검증 + if (verifiedLogoutRequest(request, response, filterChain)) return; + + // Cookie에서 Refresh Token 가져오기 + String refreshToken = null; + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + doStatusBadRequest("[doFilter] cookies are null", response); + return; + } + for (Cookie cookie : cookies) { + if ("refresh".equals(cookie.getName())) { + refreshToken = cookie.getValue(); + } + } + + // Refresh Token 검증 + if (!validateRefreshToken(response, refreshToken)) return; + + // 로그아웃 진행 + log.info("[doFilter] Logging out"); + + // Refresh 토큰 DB에서 제거 + refreshTokenRepository.findByToken(refreshToken).ifPresent(refreshTokenRepository::delete); + + // Refresh 토큰 Cookie 삭제 + Cookie cookie = new Cookie("refresh", null); + cookie.setMaxAge(0); + cookie.setPath("/"); + + response.addCookie(cookie); + response.setStatus(HttpServletResponse.SC_OK); + } + + /** + * 로그아웃 요청 검증 + * @param request + * @param response + * @param filterChain + * @return boolean + * @throws IOException + * @throws ServletException + */ + private static boolean verifiedLogoutRequest(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { + // 로그아웃 요청이 /api/users/logout으로 와야 하고, 메서드는 POST여야 함 + final String requestUri = request.getRequestURI(); + if (!"/api/users/logout".equals(requestUri)) { + filterChain.doFilter(request, response); + return true; + } + final String requestMethod = request.getMethod(); + if (!"POST".equals(requestMethod)) { + filterChain.doFilter(request, response); + return true; + } + return false; + } + + /** + * Refresh Token 검증 + * @param response + * @param refreshToken + * @return + */ + private boolean validateRefreshToken(HttpServletResponse response, String refreshToken) { + // refresh token이 없는 경우 + if (refreshToken == null) { + doStatusBadRequest("[doFilter] refreshToken is null", response); + return false; + } + + // 토큰 만료 검증 + try { + jwtTokenProvider.isExpired(refreshToken); + } catch (ExpiredJwtException e) { + doStatusBadRequest("[doFilter] refreshToken is expired", response); + return false; + } + + // 토큰이 refresh 토큰인지 확인 + String category = jwtTokenProvider.getCategory(refreshToken); + if (!"refresh".equals(category)) { + doStatusBadRequest("[doFilter] token is not a refresh token", response); + return false; + } + + // DB에 저장되어 있는지 확인 + if (!refreshTokenRepository.existsByToken(refreshToken)) { + doStatusBadRequest("[doFilter] refreshToken does not exist in repository", response); + return false; + } + + return true; + } + + private static void doStatusBadRequest(String msg, HttpServletResponse response) { + log.info(msg); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + } +} diff --git a/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenFilter.java b/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtAuthenticationFilter.java similarity index 67% rename from backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenFilter.java rename to backend/src/main/java/com/metlab_project/backend/security/jwt/JwtAuthenticationFilter.java index 36397d1..47e94ac 100644 --- a/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenFilter.java +++ b/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtAuthenticationFilter.java @@ -1,19 +1,19 @@ package com.metlab_project.backend.security.jwt; +import com.metlab_project.backend.domain.dto.user.CustomUserDetails; import com.metlab_project.backend.domain.dto.user.UserInfoResponse; +import com.metlab_project.backend.domain.dto.user.UserRole; +import com.metlab_project.backend.domain.entity.User; import com.metlab_project.backend.exception.TokenException; import com.metlab_project.backend.service.jwt.BlacklistTokenService; import com.metlab_project.backend.service.user.UserService; - import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; - import lombok.RequiredArgsConstructor; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -28,14 +28,14 @@ import java.util.List; @RequiredArgsConstructor -public class JwtTokenFilter extends OncePerRequestFilter implements Filter { +public class JwtAuthenticationFilter extends OncePerRequestFilter implements Filter { private final UserService userService; private final JwtTokenProvider jwtTokenProvider; private final JwtTokenValidator jwtTokenValidator; private final BlacklistTokenService blacklistTokenService; - private static final Logger logger = LoggerFactory.getLogger(JwtTokenFilter.class); + private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class); private static final AntPathMatcher pathMatcher = new AntPathMatcher(); private static final List whiteListUrl = Arrays.asList( @@ -45,36 +45,46 @@ public class JwtTokenFilter extends OncePerRequestFilter implements Filter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException{ + throws ServletException, IOException { logger.info("Enter JwtTokenFilter with {}", request.getRequestURI()); String accessToken = getTokenFromHttpRequest(request); - if(isPermittedUrl(request.getRequestURI())){ + if (isPermittedUrl(request.getRequestURI())) { filterChain.doFilter(request, response); return; } - try{ - boolean isValidate = (accessToken != null) && (!jwtTokenValidator.validateAccessToken(accessToken)) - && blacklistTokenService.isBlacklisted(accessToken); + try { + jwtTokenValidator.validateAccessToken(accessToken); - if(isValidate){ - UserInfoResponse user = jwtTokenProvider.getUserInfoFromJwt(accessToken); - Authentication auth = new UsernamePasswordAuthenticationToken(user.getSchoolEmail(), null, Collections.emptyList()); - SecurityContextHolder.getContext().setAuthentication(auth); - } - }catch(TokenException e){ + String username = jwtTokenProvider.getUserInfo(accessToken).getSchoolEmail(); + UserRole userRole = jwtTokenProvider.getUserInfo(accessToken).getRole(); + + User user = User.builder() + .schoolEmail(username) + .role(userRole) + .build(); + + CustomUserDetails customUserDetails = new CustomUserDetails(user); + + Authentication authentication = new UsernamePasswordAuthenticationToken(customUserDetails,null, customUserDetails.getAuthorities()); + + SecurityContextHolder.getContext().setAuthentication(authentication); + + filterChain.doFilter(request,response); + + } catch (TokenException e) { logger.warn("JwtException Occurred"); - handleTokenException(request,response, e, accessToken); - }catch (Exception e){ + handleTokenException(request, response, e, accessToken); + } catch (Exception e) { SecurityContextHolder.clearContext(); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.getWriter().write("UnExpected Error with : " + e.getMessage()); } } - private String getTokenFromHttpRequest(HttpServletRequest request){ + private String getTokenFromHttpRequest(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { @@ -87,19 +97,19 @@ private String getTokenFromHttpRequest(HttpServletRequest request){ return null; } - private boolean isPermittedUrl(String requestUri){ + private boolean isPermittedUrl(String requestUri) { return whiteListUrl.stream() .anyMatch(uri -> pathMatcher.match(uri, requestUri)); } - private void handleTokenException(HttpServletRequest request, HttpServletResponse response, TokenException e, String accessToken) throws IOException{ - switch (e.getErrorCode()){ - case TOKEN_EXPIRED: - try{ + private void handleTokenException(HttpServletRequest request, HttpServletResponse response, TokenException e, String accessToken) throws IOException { + switch (e.getErrorCode()) { + case TOKEN_EXPIRED: + try { String schoolEmail = jwtTokenProvider.getSchoolEmailFromExpiredToken(accessToken); String refreshToken = userService.getRefreshToken(schoolEmail); - if((refreshToken != null) && jwtTokenValidator.validateRefreshToken(refreshToken)){ + if ((refreshToken != null) && jwtTokenValidator.validateRefreshToken(refreshToken)) { UserInfoResponse user = userService.getUserInfoBySchoolEmail(schoolEmail); String newAccessToken = jwtTokenProvider.generateAccessToken(user.getSchoolEmail(), user.getNickname(), user.getGender(), user.getStudentId(), user.getCollege(), user.getDepartment()); @@ -107,14 +117,14 @@ private void handleTokenException(HttpServletRequest request, HttpServletRespons Cookie cookie = new Cookie("JWT", newAccessToken); cookie.setHttpOnly(true); cookie.setPath("/"); - cookie.setMaxAge(60*60); + cookie.setMaxAge(60 * 60); response.addCookie(cookie); Authentication auth = new UsernamePasswordAuthenticationToken(user.getSchoolEmail(), null, Collections.emptyList()); SecurityContextHolder.getContext().setAuthentication(auth); } - }catch(TokenException error){ + } catch (TokenException error) { SecurityContextHolder.clearContext(); Cookie jwtCookie = new Cookie("JWT", null); @@ -126,16 +136,17 @@ private void handleTokenException(HttpServletRequest request, HttpServletRespons response.getWriter().write("Token error: " + error.getMessage()); } break; - case TOKEN_INVALID: - sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "Invalid token"); - break; - case TOKEN_MISSING: - sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST, "Token is missing"); - break; - default: - sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An unexpected error occurred"); - } - + case TOKEN_INVALID: + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "Invalid token"); + break; + case TOKEN_MISSING: + sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST, "Token is missing"); + break; + case TOKEN_BLACKLISTED: + sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST, "Token is blackListed"); + default: + sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An unexpected error occurred"); + } } private void sendErrorResponse(HttpServletResponse response, int status, String message) throws IOException { diff --git a/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenProvider.java b/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenProvider.java index 1b81301..8a55c45 100644 --- a/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenProvider.java +++ b/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenProvider.java @@ -2,6 +2,7 @@ import com.metlab_project.backend.domain.dto.user.UserInfoResponse; +import com.metlab_project.backend.domain.dto.user.UserRole; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; @@ -46,6 +47,7 @@ public String generateAccessToken(String schoolEmail, String nickname, String ge claims.put("gender", gender); claims.put("college", college); claims.put("department", department); + claims.put("role", UserRole.ROLE_USER); String token = Jwts.builder() .setHeader(header) @@ -67,21 +69,24 @@ public String generateRefreshToken(String schoolEmail){ .compact(); } - public UserInfoResponse getUserInfoFromJwt(String accessToken){ + public UserInfoResponse getUserInfo(String accessToken){ Claims claims = Jwts.parserBuilder() .setSigningKey(accessKey) .build() .parseClaimsJws(accessToken) .getBody(); - return new UserInfoResponse( - claims.getSubject(), - claims.get("nickname", String.class), - claims.get("gender", String.class), - claims.get("studentId", String.class), - claims.get("college", String.class), - claims.get("department", String.class) - ); + + return UserInfoResponse.builder() + .schoolEmail(claims.getSubject()) + .nickname(claims.get("nickname", String.class)) + .gender(claims.get("gender", String.class)) + .studentId(claims.get("studentId", String.class)) + .college(claims.get("college", String.class)) + .department(claims.get("department", String.class)) + .role(claims.get("role", UserRole.class)) + .build(); + } public String getSchoolEmailFromExpiredToken(String token){ @@ -105,6 +110,20 @@ public Date getExpirationDateFromToken(String token){ .getBody().getExpiration(); } + public boolean isExpired(String token) { + Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + public String getCategory(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(accessKey) // or refreshKey depending on the token type + .build() + .parseClaimsJws(token) + .getBody(); + return claims.get("category", String.class); + } + private Map createJwtHeader(){ Map header = new HashMap<>(); header.put("typ", "JWT"); diff --git a/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenValidator.java b/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenValidator.java index 66a38a2..0b58e3d 100644 --- a/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenValidator.java +++ b/backend/src/main/java/com/metlab_project/backend/security/jwt/JwtTokenValidator.java @@ -1,12 +1,15 @@ package com.metlab_project.backend.security.jwt; import com.metlab_project.backend.exception.TokenException; +import com.metlab_project.backend.service.jwt.BlacklistTokenService; import io.jsonwebtoken.*; import io.jsonwebtoken.security.SignatureException; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component +@RequiredArgsConstructor public class JwtTokenValidator { @Value("${jwt.secret.access}") @@ -15,7 +18,12 @@ public class JwtTokenValidator { @Value("${jwt.secret.refresh}") private String refreshTokenSecret; + private final BlacklistTokenService blacklistTokenService; + public boolean validateAccessToken(String token) { + if (blacklistTokenService.isBlacklisted(token)) + throw new TokenException(TokenException.TokenErrorCode.TOKEN_BLACKLISTED); + try { Claims claims = Jwts.parserBuilder() .setSigningKey(accessTokenSecret.getBytes()) diff --git a/backend/src/main/java/com/metlab_project/backend/service/user/CustomUserDetailsService.java b/backend/src/main/java/com/metlab_project/backend/service/user/CustomUserDetailsService.java new file mode 100644 index 0000000..1b9a6f9 --- /dev/null +++ b/backend/src/main/java/com/metlab_project/backend/service/user/CustomUserDetailsService.java @@ -0,0 +1,24 @@ +package com.metlab_project.backend.service.user; + +import com.metlab_project.backend.domain.dto.user.CustomUserDetails; +import com.metlab_project.backend.domain.entity.User; +import com.metlab_project.backend.repository.user.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CustomUserDetailsService implements UserDetailsService { + private final UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{ + User user = userRepository.findBySchoolEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("Find User by Username(SchoolEmail) is failed" + username)); + + return new CustomUserDetails(user); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/metlab_project/backend/service/user/UserService.java b/backend/src/main/java/com/metlab_project/backend/service/user/UserService.java index 30cdc29..e77b6e5 100644 --- a/backend/src/main/java/com/metlab_project/backend/service/user/UserService.java +++ b/backend/src/main/java/com/metlab_project/backend/service/user/UserService.java @@ -5,7 +5,6 @@ import com.metlab_project.backend.repository.user.UserRepository; import lombok.RequiredArgsConstructor; - import org.springframework.security.authentication.BadCredentialsException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,7 +15,7 @@ public class UserService { private final UserRepository userRepository; - public UserInfoResponse getUserInfoBySchoolEmail(String schoolEmail){ + public UserInfoResponse getUserInfoBySchoolEmail(String schoolEmail) { User user = userRepository.findBySchoolEmail(schoolEmail) .orElseThrow(() -> new BadCredentialsException("Invalid Email")); @@ -30,10 +29,35 @@ public UserInfoResponse getUserInfoBySchoolEmail(String schoolEmail){ .build(); } - public String getRefreshToken(String schoolEmail){ + public String getRefreshToken(String schoolEmail) { User user = userRepository.findBySchoolEmail(schoolEmail) .orElseThrow(() -> new BadCredentialsException("Invalid Email")); return user.getRefreshtoken(); } + + public UserInfoResponse updateUserInfo(String schoolEmail, UserInfoResponse userInfoResponse) { + User user = userRepository.findBySchoolEmail(schoolEmail) + .orElseThrow(() -> new RuntimeException("User not found")); + + // User 엔티티의 정보를 UserInfoResponse의 값으로 업데이트 + user.setNickname(userInfoResponse.getNickname()); + user.setGender(userInfoResponse.getGender()); + user.setStudentId(userInfoResponse.getStudentId()); + user.setCollege(userInfoResponse.getCollege()); + user.setDepartment(userInfoResponse.getDepartment()); + + // 저장 + User updatedUser = userRepository.save(user); + + // UserInfoResponse로 변환하여 반환 + return UserInfoResponse.builder() + .schoolEmail(updatedUser.getSchoolEmail()) + .nickname(updatedUser.getNickname()) + .gender(updatedUser.getGender()) + .studentId(updatedUser.getStudentId()) + .college(updatedUser.getCollege()) + .department(updatedUser.getDepartment()) + .build(); + } }