Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Spring Security + JWT Cookie 일반 로그인 구현 #20

Merged
merged 6 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions omocha-client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

// jwt
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'

// gson
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.9'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.auction.client.auction.application;

import static org.auction.client.common.code.AuctionErrorCode.*;
import static org.auction.client.common.code.AuctionCode.*;
import static org.auction.client.common.code.ImageCode.*;
import static org.auction.client.common.code.MemberCode.*;

import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -9,17 +11,18 @@
import org.auction.client.auction.interfaces.response.AuctionDetailResponse;
import org.auction.client.auction.interfaces.response.CreateAuctionResponse;
import org.auction.client.exception.auction.AuctionNotFoundException;
import org.auction.client.exception.auction.ImageDeletionException;
import org.auction.client.exception.auction.ImageNotFoundException;
import org.auction.client.exception.auction.MemberNotFoundException;
import org.auction.client.exception.image.ImageDeletionException;
import org.auction.client.exception.image.ImageNotFoundException;
import org.auction.client.exception.member.InvalidMemberException;
import org.auction.client.exception.member.MemberNotFoundException;
import org.auction.client.image.application.AwsS3Service;
import org.auction.domain.auction.domain.entity.AuctionEntity;
import org.auction.domain.auction.domain.enums.AuctionStatus;
import org.auction.domain.auction.infrastructure.AuctionRepository;
import org.auction.domain.image.domain.entity.ImageEntity;
import org.auction.domain.image.infrastructure.ImageRepository;
import org.auction.domain.user.domain.entity.MemberEntity;
import org.auction.domain.user.infrastructure.MemberRepository;
import org.auction.domain.member.domain.entity.MemberEntity;
import org.auction.domain.member.infrastructure.MemberRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
Expand Down Expand Up @@ -114,11 +117,16 @@ public AuctionDetailResponse findAuctionDetails(

@Transactional
public void removeAuction(
Long memberId,
Long auctionId
) {
AuctionEntity auctionEntity = auctionRepository.findById(auctionId)
.orElseThrow(() -> new AuctionNotFoundException(AUCTION_NOT_FOUND));

if (!auctionEntity.getMemberEntity().getMemberId().equals(memberId)) {
throw new InvalidMemberException(INVALID_MEMBER);
}

try {
// 이미지 삭제
List<ImageEntity> images = auctionEntity.getImages();
Expand All @@ -131,6 +139,7 @@ public void removeAuction(

log.debug("Remove auction finished with auctionId {}", auctionId);

// TODO: soft delete로 변경해야 함
auctionRepository.delete(auctionEntity);

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.auction.client.auction.interfaces;

import java.security.Principal;
import java.util.List;

import org.auction.client.auction.interfaces.request.CreateAuctionRequest;
Expand Down Expand Up @@ -31,8 +32,8 @@ public interface AuctionApi {
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResultDto.class)))
})
ResponseEntity<ResultDto<CreateAuctionResponse>> auctionSave(
@Parameter(description = "사용자 ID", required = true)
Long memberId,
@Parameter(description = "사용자 객체 정보", required = true)
Principal principal,

@Parameter(description = "경매 요청 데이터", required = true)
CreateAuctionRequest auctionRequest,
Expand Down Expand Up @@ -73,6 +74,9 @@ ResponseEntity<ResultDto<AuctionDetailResponse>> auctionDetails(
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResultDto.class)))
})
ResponseEntity<ResultDto<Void>> auctionRemove(
@Parameter(description = "사용자 객체 정보", required = true)
Principal principal,

@Parameter(description = "경매 ID", required = true)
Long auctionId
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package org.auction.client.auction.interfaces;

import static org.auction.client.common.code.ResponseCode.*;
import static org.auction.client.common.code.AuctionCode.*;

import java.security.Principal;
import java.util.List;

import org.auction.client.auction.application.AuctionService;
Expand Down Expand Up @@ -33,22 +34,23 @@ public class AuctionController implements AuctionApi {

// REFACTOR : 로그인 구현이 완료되면 PathVariable에 있는 User Id를 삭제할 예정
@Override
@PostMapping(value = "/{member_id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = "/", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResultDto<CreateAuctionResponse>> auctionSave(
@PathVariable("member_id") Long memberId,
Principal principal,
@RequestPart("auctionRequest") CreateAuctionRequest auctionRequest,
@RequestPart(value = "images", required = true) List<MultipartFile> images
) {
log.info("Received CreateAuctionRequest: {}", auctionRequest);
log.debug("Create auction post started");

// TODO : 소셜로그인 구현 시 SecurityContextHolder에서 User 정보를 가져올 수 있다.
Long memberId = Long.parseLong(principal.getName());

CreateAuctionResponse response = auctionService.addAuction(auctionRequest, images, memberId);

ResultDto<CreateAuctionResponse> resultDto = ResultDto.res(
AUCTION_CREATE_SUCCESS.getStatusCode(),
AUCTION_CREATE_SUCCESS.getMessage(),
AUCTION_CREATE_SUCCESS.getResultMsg(),
response
);

Expand All @@ -69,7 +71,7 @@ public ResponseEntity<ResultDto<AuctionDetailResponse>> auctionDetails(

ResultDto<AuctionDetailResponse> resultDto = ResultDto.res(
AUCTION_DETAIL_SUCCESS.getStatusCode(),
AUCTION_DETAIL_SUCCESS.getMessage(),
AUCTION_DETAIL_SUCCESS.getResultMsg(),
response
);

Expand All @@ -81,15 +83,16 @@ public ResponseEntity<ResultDto<AuctionDetailResponse>> auctionDetails(
@Override
@DeleteMapping("/{auction_id}")
public ResponseEntity<ResultDto<Void>> auctionRemove(
Principal principal,
@PathVariable("auction_id") Long auctionId
) {
log.debug("Remove auction post started");

auctionService.removeAuction(auctionId);
auctionService.removeAuction(Long.parseLong(principal.getName()), auctionId);

ResultDto<Void> resultDto = ResultDto.res(
AUCTION_DELETE_SUCCESS.getStatusCode(),
AUCTION_DELETE_SUCCESS.getMessage(),
AUCTION_DELETE_SUCCESS.getResultMsg(),
null
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import java.time.LocalDateTime;

import org.auction.domain.auction.domain.enums.AuctionType;
import org.springframework.format.annotation.DateTimeFormat;

import com.fasterxml.jackson.annotation.JsonFormat;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
Expand All @@ -15,9 +16,9 @@ public record CreateAuctionRequest(
@NotNull Integer startPrice,
@NotNull Integer bidUnit,
@NotNull AuctionType auctionType,
@NotNull @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@NotNull @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
LocalDateTime startDate,
@NotNull @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@NotNull @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
LocalDateTime endDate
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.auction.client.common.code;

import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum AuctionCode {

// EXPLAIN: 200 OK 응답 코드
AUCTION_CREATE_SUCCESS(HttpStatus.OK, "경매가 성공적으로 생성되었습니다."),
AUCTION_DELETE_SUCCESS(HttpStatus.OK, "경매가 성공적으로 삭제되었습니다."),
AUCTION_DETAIL_SUCCESS(HttpStatus.OK, "경매 상세 정보 조회 성공"),

// EXPLAIN: 404 ERROR
AUCTION_NOT_FOUND(HttpStatus.NOT_FOUND, "경매 게시물을 찾을 수 없습니다."),

// EXPLAIN: 415 ERROR
UNSUPPORTED_MEDIA_TYPE(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "지원하지 않는 Content-Type 입니다."),

// EXPLAIN: 500 SERVER ERROR
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버에 오류가 발생했습니다.");

private final HttpStatus httpStatus;
private final String resultMsg;

// 필요하다면 상태 코드를 반환하는 메서드 추가
public int getStatusCode() {
return httpStatus.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.auction.client.common.code;

import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ImageCode {

// EXPLAIN: 404 ERROR
IMAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "이미지를 찾을 수 없습니다."),

// EXPLAIN: 500 SERVER ERROR
IMAGE_DELETION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "이미지 삭제 중 오류가 발생했습니다.");

private final HttpStatus httpStatus;
private final String resultMsg;

// 필요하다면 상태 코드를 반환하는 메서드 추가
public int getStatusCode() {
return httpStatus.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.auction.client.common.code;

import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum JwtCode {

// EXPLAIN: 400 ERROR
INVALID_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, "올바르지 않는 Refresh Token 입니다."),

// EXPLAIN: 404 ERROR
JWT_TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "JWT 토큰을 찾을 수 없습니다.");

private final HttpStatus httpStatus;
private final String resultMsg;

// 필요하다면 상태 코드를 반환하는 메서드 추가
public int getStatusCode() {
return httpStatus.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.auction.client.common.code;

import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum MemberCode {

// EXPLAIN: 200 OK 응답 코드
MEMBER_CREATE_SUCCESS(HttpStatus.OK, "회원을 성공적으로 생성하였습니다."),
MEMBER_LOGIN_SUCCESS(HttpStatus.OK, "로그인이 완료되었습니다."),

// EXPLAIN: 400 ERROR
MEMBER_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, "이미 존재하는 회원입니다."),
INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "비밀번호가 일치하지 않습니다."),
INVALID_MEMBER(HttpStatus.BAD_REQUEST, "회원이 일치하지 않습니다."),

// EXPLAIN: 404 ERROR
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "회원을 찾을 수 없습니다.");

private final HttpStatus httpStatus;
private final String resultMsg;

// 필요하다면 상태 코드를 반환하는 메서드 추가
public int getStatusCode() {
return httpStatus.value();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@
@AllArgsConstructor
public enum ResponseCode {

// EXPLAIN: 200 OK 응답 코드
AUCTION_CREATE_SUCCESS(HttpStatus.OK, "경매가 성공적으로 생성되었습니다."),
AUCTION_DELETE_SUCCESS(HttpStatus.OK, "경매가 성공적으로 삭제되었습니다."),
AUCTION_DETAIL_SUCCESS(HttpStatus.OK, "경매 상세 정보 조회 성공"),

// EXPLAIN: 400 Bad Request 응답 코드
BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."),

Expand All @@ -24,7 +19,7 @@ public enum ResponseCode {
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 오류가 발생했습니다.");

private final HttpStatus httpStatus;
private final String message;
private final String resultMsg;

public int getStatusCode() {
return httpStatus.value();
Expand Down
Loading