Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public enum ErrorCode {
FILE_UPLOAD_FAILED(HttpStatus.BAD_REQUEST, "파일 업로드에 실패했습니다."),
FILE_DELETE_FAILED(HttpStatus.BAD_REQUEST, "파일 삭제에 실패하였습니다."),

// REDIS 오류
REDIS_CONNECTION_FAILURE(HttpStatus.INTERNAL_SERVER_ERROR,"Redis에 연결할 수 없습니다."),
// 기타 오류
UNAUTHORIZED_ACCESS(HttpStatus.FORBIDDEN, "접근 권한이 없습니다."),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류가 발생했습니다.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,31 +93,27 @@ public ResponseEntity<LoginResDto> signUp(@Valid @RequestBody SignUpReqDto signU
public ResponseEntity<?> login(@RequestBody EmailLoginReqDto loginReqDto,
HttpServletRequest request,
HttpServletResponse response) {
MemberDto memberDto = authService.authenticateMember(loginReqDto);
try {
String accessToken = getJwtFromRequest(request);

// 액세스 토큰이 유효한 경우
if (StringUtils.hasText(accessToken) && tokenProvider.validateAccessToken(accessToken)) {
// 기존의 액세스 토큰과 사용자 정보를 반환
return ResponseEntity.ok(authService.handleExistingToken(accessToken));
MemberDto authenticatedMember = authService.authenticateMember(loginReqDto);
String accessToken = tokenProvider.extractToken(request);

if (StringUtils.hasText(accessToken)) {
if (tokenProvider.validateAccessToken(accessToken)) {
// 유효한 토큰이 있는 경우, 기존 토큰 정보 반환
return ResponseEntity.ok(authService.handleExistingToken(accessToken));
} else {
// 토큰이 만료된 경우, 401 에러 반환
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("토큰이 만료되었습니다.");
}
} else {
// 토큰이 없는 경우, 새로운 로그인 프로세스 진행
return ResponseEntity.ok(authService.login(authenticatedMember, request, response));
}
// 액세스 토큰이 없거나 유효하지 않은 경우, 새로운 로그인 처리
LoginResDto loginResDto = authService.login(memberDto, request, response);
return ResponseEntity.ok(loginResDto);

} catch (CustomException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body("Unauthorized: " + e.getMessage());
}
}

private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7); // "Bearer " 부분을 제거하고 토큰 반환
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Forbidden: " + e.getMessage());
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인 정보가 유효하지 않습니다.");
}
return null; // 토큰이 없으면 null 반환
}

/**
Expand Down Expand Up @@ -152,9 +148,8 @@ private String getRefreshTokenFromCookies(HttpServletRequest request) {
@PostMapping("/logout")
public ResponseEntity<?> logout(@AuthenticationPrincipal MemberDetails memberDetails,
HttpServletResponse response) {
String email = memberDetails.getUsername();

authService.logout(email, response);
authService.logout(memberDetails.getMemberId(), response);

return ResponseEntity.ok("로그아웃이 완료되었습니다.");
}
Expand All @@ -168,7 +163,7 @@ public ResponseEntity<?> withdrawMember(@AuthenticationPrincipal MemberDetails m
HttpServletResponse response) {
authService.withdraw(memberDetails.getMemberId(), response);

return ResponseEntity.ok().body("회원탈퇴가 완료되었습니다.");
return ResponseEntity.ok("회원탈퇴가 완료되었습니다.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public ResponseEntity<Page<MemberDto>> getMembers(Pageable pageable) {
*/
@GetMapping("/{memberId}")
public MemberDto getMemberById(@PathVariable Long memberId) {
return memberService.getMemberById(memberId);
return memberService.getMember(memberId);
}

/**
Expand All @@ -55,8 +55,7 @@ public ResponseEntity<?> getMyInfo(@AuthenticationPrincipal MemberDetails member
if (memberDetails == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
Long memberId = memberDetails.getMemberId();
return ResponseEntity.ok(memberService.getMyInfo(memberId));
return ResponseEntity.ok(memberService.getMyInfo(memberDetails.getMemberId()));
}

/**
Expand All @@ -68,9 +67,8 @@ public ResponseEntity<Page<ReservationResDto>> getMyReservations(Pageable pageab
if (memberDetails == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String email = memberDetails.getUsername();

return ResponseEntity.ok(memberService.getMyReservations(pageable, email));
return ResponseEntity.ok(memberService.getMyReservations(pageable, memberDetails.getMemberId()));
}

/**
Expand All @@ -79,8 +77,7 @@ public ResponseEntity<Page<ReservationResDto>> getMyReservations(Pageable pageab
@GetMapping("/me/reviews")
public ResponseEntity<Page<ReviewDetailDto>> getMyReviews(Pageable pageable,
@AuthenticationPrincipal MemberDetails memberDetails) {
String email = memberDetails.getUsername();
return ResponseEntity.ok(memberService.getMyReviews(pageable, email));
return ResponseEntity.ok(memberService.getMyReviews(pageable, memberDetails.getMemberId()));
}

/**
Expand All @@ -92,9 +89,8 @@ public ResponseEntity<Page<ChatRoomCreateResDto>> getMyChats(Pageable pageable,
if (memberDetails == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String email = memberDetails.getUsername();

Page<ChatRoomCreateResDto> rooms = memberService.getMyChatRooms(pageable, email);
Page<ChatRoomCreateResDto> rooms = memberService.getMyChatRooms(pageable, memberDetails.getMemberId());

return ResponseEntity.ok(rooms);
}
Expand All @@ -108,7 +104,6 @@ public ResponseEntity<Map<String, String>> uploadFile(@AuthenticationPrincipal M
if (memberDetails == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String email = memberDetails.getUsername();

try {
String fileUrl = imageUploadService.uploadProfileImage(file, memberDetails.getMemberId());
Expand All @@ -131,8 +126,7 @@ public ResponseEntity<Map<String, String>> uploadFile(@AuthenticationPrincipal M
* 새로운 이미지 업로드 (S3)
*/
@PutMapping("/me/profile-image")
public ResponseEntity<?> updateProfileImage(@AuthenticationPrincipal MemberDetails memberDetails,
@RequestParam("file") MultipartFile file) throws IOException {
public ResponseEntity<?> updateProfileImage(@AuthenticationPrincipal MemberDetails memberDetails, @RequestParam("file") MultipartFile file) throws IOException {
return ResponseEntity.ok(imageUploadService.updateProfileImage(file, memberDetails.getMemberId()));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.zero.bwtableback.member.dto;

import com.zero.bwtableback.member.entity.LoginType;
import com.zero.bwtableback.member.entity.Member;
import com.zero.bwtableback.member.entity.Role;
import lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -16,4 +17,18 @@ public MemberPrivateDto(Long id, String email, String name, String nickname,
super(id, email, name, nickname, phone, role, profileImage, businessNubmer);
this.loginType = loginType;
}

public static MemberPrivateDto from(Member member) {
return new MemberPrivateDto(
member.getId(),
member.getEmail(),
member.getName(),
member.getNickname(),
member.getPhone(),
member.getRole(),
member.getProfileImage(),
member.getBusinessNumber(),
member.getLoginType()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ public class KakaoOAuth2Controller {
public ResponseEntity<?> kakaoLogin(@RequestParam(required = false) String code,
HttpServletRequest request,
HttpServletResponse response) throws JsonProcessingException {
// 요청 헤더에서 액세스 토큰 추출
String accessToken = getJwtFromRequest(request);

// 첫 번째 로그인: 카카오에서 정보를 추출하여 서버에 회원가입
if (code != null) {
String kakaoToken = kakaoService.getAccessToken(code);
Expand All @@ -52,24 +49,23 @@ public ResponseEntity<?> kakaoLogin(@RequestParam(required = false) String code,

return ResponseEntity.ok(loginResDto);
} else {
// 액세스 토큰 존재하고 유효한 경우
if (StringUtils.hasText(accessToken) && tokenProvider.validateAccessToken(accessToken)) {
// 기존의 액세스 토큰과 사용자 정보를 반환
LoginResDto loginResDto = authService.handleExistingToken(accessToken);

return ResponseEntity.ok(loginResDto);
// 카카오 인가 코드가 없는 경우
String accessToken = tokenProvider.extractToken(request);
if (StringUtils.hasText(accessToken)) {
if (tokenProvider.validateAccessToken(accessToken)) {
// 유효한 액세스 토큰이 있는 경우
LoginResDto loginResDto = authService.handleExistingToken(accessToken);
return ResponseEntity.ok(loginResDto);
} else {
// 액세스 토큰이 만료된 경우
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body("토큰이 만료되었습니다.");
}
} else {
// 액세스 토큰이 없는 경우
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body("토큰이 존재하지 않습니다.");
}
// 토큰이 없거나 유효하지 않은 경우 401 응답을 던짐
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body("Unauthorized. 토큰이 유효하지 않습니다.");
}
}

private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7); // "Bearer " 부분을 제거하고 토큰 반환
}
return null; // 토큰이 없으면 null 반환
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ private MemberDto registerNewMember(KakaoUserInfoDto userInfo) {
.nickname(userInfo.getNickName())
.phone(userInfo.getPhone())
.role(Role.GUEST)
.status(Status.ACTIVE)
.provider(userInfo.getProvider())
.providerId(userInfo.getProviderId())
.profileImage(userInfo.getProfileImage())
Expand Down
49 changes: 25 additions & 24 deletions src/main/java/com/zero/bwtableback/member/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,26 +123,27 @@ public LoginResDto signUpLogin(MemberDto memberDto, HttpServletRequest request,
* 로그인
*/
public LoginResDto login(MemberDto memberDto, HttpServletRequest request, HttpServletResponse response) {
try {
String accessToken = tokenProvider.createAccessToken(memberDto.getEmail(), memberDto.getRole());
String refreshToken = tokenProvider.createRefreshToken(memberDto.getId().toString());

String accessToken = tokenProvider.createAccessToken(memberDto.getEmail(), memberDto.getRole());
String refreshToken = tokenProvider.createRefreshToken(memberDto.getId().toString());

// 회원 상태 조회

// HttpOnly 쿠키에 리프레시 토큰 저장
saveRefreshTokenToCookie(refreshToken, response);

// Redis에 리프레시 토큰 저장
saveRefreshTokenToRedis(memberDto.getId(), refreshToken);
// HttpOnly 쿠키에 리프레시 토큰 저장
saveRefreshTokenToCookie(refreshToken, response);

// 레스토랑 ID 조회 (사장님일 경우)
Long restaurantId = getRestaurantIdIfOwner(memberDto);
// Redis에 리프레시 토큰 저장
saveRefreshTokenToRedis(memberDto.getId(), refreshToken);

// 레스토랑 ID 조회 (사장님일 경우)
Long restaurantId = getRestaurantIdIfOwner(memberDto);

return new LoginResDto(accessToken, memberDto, restaurantId);
return new LoginResDto(accessToken, memberDto, restaurantId);
} catch (Exception e) {
log.error("로그인 실패: {}", memberDto.getEmail(), e);
throw new IllegalArgumentException("로그인 실패", e);
}
}

// 회원 인증
// 이메일과 비밀번호 검증
public MemberDto authenticateMember(EmailLoginReqDto loginReqDto) {
Member member = memberRepository.findByEmail(loginReqDto.getEmail())
.orElseThrow(() -> new CustomException(ErrorCode.INVALID_CREDENTIALS));
Expand All @@ -162,7 +163,6 @@ public MemberDto authenticateMember(EmailLoginReqDto loginReqDto) {
// 리프레시 토큰으로 액세스 토큰 갱신
public LoginResDto renewAccessTokenWithRefreshToken(String refreshToken) {
String email = tokenProvider.getUsername(refreshToken);

Member member = memberRepository.findByEmail(email)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

Expand All @@ -178,7 +178,6 @@ public LoginResDto renewAccessTokenWithRefreshToken(String refreshToken) {
// 리프레시 토큰 검증
public void validateRefreshToken(String refreshToken, Long memberId) {
String key = "refresh_token:" + memberId;

String storedRefreshToken = redisTemplate.opsForValue().get(key);

if (storedRefreshToken == null || !storedRefreshToken.equals(refreshToken)) {
Expand All @@ -202,10 +201,9 @@ public void saveRefreshTokenToRedis(Long memberId, String refreshToken) {
try {
redisTemplate.opsForValue().set(key, refreshToken);
} catch (RedisConnectionFailureException e) {
System.err.println("Redis에 연결할 수 없습니다: " + e.getMessage());
throw new CustomException(ErrorCode.REDIS_CONNECTION_FAILURE);
} catch (Exception e) {
// 다른 예외 처리
System.err.println("예기치 않은 오류 발생: " + e.getMessage());
throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR);
}
}

Expand Down Expand Up @@ -234,9 +232,8 @@ private Long getRestaurantIdIfOwner(MemberDto member) {
/**
* 사용자 로그아웃 처리
*/
public void logout(String email, HttpServletResponse response) {
Member member = memberRepository.findByEmail(email)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
public void logout(Long memberId, HttpServletResponse response) {
Member member = getMemberById(memberId);

String key = "refresh_token:" + member.getId();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인자로 받은 memberId를 직접 사용하지 않은 이유가 있을까요?

Copy link
Contributor Author

@blubincod blubincod Dec 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

차이는 없는 걸로 알고있습니다. 저는 데이터의 일관성과 엔티티 객체를 통해 데이터에 접근하는 것이 객체 지향 프로그래밍의 원칙에 더 부합하고 알고 있어서 사용하였습니다.

redisTemplate.delete(key);
Expand All @@ -255,8 +252,7 @@ public void logout(String email, HttpServletResponse response) {
* 로그아웃 처리 후
*/
public void withdraw(Long memberId, HttpServletResponse response) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
Member member = getMemberById(memberId);

String key = "refresh_token:" + member.getId();
redisTemplate.delete(key);
Expand All @@ -274,4 +270,9 @@ public void withdraw(Long memberId, HttpServletResponse response) {
member.setStatus(Status.INACTIVE);
memberRepository.save(member);
}

public Member getMemberById(Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
}
}
Loading