diff --git a/fourthSeminar/build.gradle b/fourthSeminar/build.gradle index a141dea..224951d 100644 --- a/fourthSeminar/build.gradle +++ b/fourthSeminar/build.gradle @@ -31,6 +31,11 @@ dependencies { implementation 'mysql:mysql-connector-java:8.0.32' // Validation implementation 'org.springframework.boot:spring-boot-starter-validation' + //JWT + implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2' + implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2' + implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' + } tasks.named('test') { diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/common/advice/ControllerExceptionAdvice.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/common/advice/ControllerExceptionAdvice.java index 846360f..6d206b1 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/common/advice/ControllerExceptionAdvice.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/common/advice/ControllerExceptionAdvice.java @@ -10,12 +10,31 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import sopt.org.fourthSeminar.common.dto.ApiResponse; import sopt.org.fourthSeminar.exception.Error; +import sopt.org.fourthSeminar.exception.model.BadRequestException; import sopt.org.fourthSeminar.exception.model.SoptException; import java.util.Objects; @RestControllerAdvice +// public class ControllerExceptionAdvice { + /** + * Spring 예외 처리 과정 + * [1] 동작 + * 1. 예외가 발생한 컨트롤러 내부에 적합한 @ExceptionHandler가 있는 지 확인 후 처리 + * 2. 없으면, ControllerAdvice로 넘어감 + * 3. ControllerAdvice안에 적합한 @ExceptionHandler가 있으면 처리 후 없으면 다음 Resolver로 넘어감 + * [2] 가 동작함 + * 1. @ResponseStatus가 있는지 또는 ResponseStatusException인지 검사함 + * 2. 맞으면 ServletResponse의 sendError()로 예외를 서블릿까지 전달되고, 서블릿이 BasicErrorController로 요청을 전달함 + * [3] 가 동작함 - 스프링 내부 기본 예외 처리 + * - Spring의 내부 예외인지 검사하여 맞으면 에러를 처리하고 아니면 넘어감 + * [4] 적합한 ExceptionResolver가 없으므로 예외가 서블릿까지 전달되고, 서블릿은 SpringBoot가 진행한 자동 설정에 맞게 BasicErrorController로 요청을 다시 전달함 + + !!가장 구체적인 예외 핸들러를 먼저 찾고 -> 없으면 부모 예외의 핸들러를 찾는다. + + 500에러는 주석 처리하고 최대한 많은 에러를 발생시켜보면서 꼼꼼하게 처리하기 + */ /** * 400 BAD_REQUEST */ @@ -26,6 +45,12 @@ protected ApiResponse handleMethodArgumentNotValidException(final MethodArgument return ApiResponse.error(Error.REQUEST_VALIDATION_EXCEPTION, String.format("%s. (%s)", fieldError.getDefaultMessage(), fieldError.getField())); } + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(BadRequestException.class) + protected ApiResponse handleBadRequestException(final BadRequestException e) { + return ApiResponse.error(e.getError(), e.getMessage()); + } + /** * 500 Internal Server */ diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/WebConfig.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/WebConfig.java new file mode 100644 index 0000000..09dfd8b --- /dev/null +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/WebConfig.java @@ -0,0 +1,21 @@ +package sopt.org.fourthSeminar.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import sopt.org.fourthSeminar.config.resolver.UserIdResolver; + +import java.util.List; + +@RequiredArgsConstructor +@Configuration +public class WebConfig implements WebMvcConfigurer { //스프링에 직접 만든 Resolver를 등록 + + private final UserIdResolver userIdResolver; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(userIdResolver); + } +} diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/jwt/JwtService.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/jwt/JwtService.java new file mode 100644 index 0000000..4dd1a87 --- /dev/null +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/jwt/JwtService.java @@ -0,0 +1,82 @@ +package sopt.org.fourthSeminar.config.jwt; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import sopt.org.fourthSeminar.exception.Error; +import sopt.org.fourthSeminar.exception.model.UnauthorizedException; + +import javax.annotation.PostConstruct; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.Base64; +import java.util.Date; + +@Service +public class JwtService { + + @Value("${jwt.secret}") + private String jwtSecret; + + @PostConstruct //의존성 주입이 이루어진 후 실행됨이 보장됨 -> 오직 한 번만 수행 + protected void init() { + jwtSecret = Base64.getEncoder() + .encodeToString(jwtSecret.getBytes(StandardCharsets.UTF_8)); + } + + // JWT 토큰 발급 + public String issuedToken(String userId) { + final Date now = new Date(); + + // 클레임 생성 -> payload + final Claims claims = Jwts.claims() + .setSubject("access_token") + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + 120 * 60 * 1000L)); //1000 (밀리초) -> 2시간 + + //private claim 등록 + claims.put("userId", userId); + + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) + .setClaims(claims) //claim 지정 + .signWith(getSigningKey()) //byte값으로 키 생성 + .compact(); + } + + private Key getSigningKey() { + final byte[] keyBytes = jwtSecret.getBytes(StandardCharsets.UTF_8); + return Keys.hmacShaKeyFor(keyBytes); + } + + // JWT 토큰 검증 (클라이언트가 가져옴) + public boolean verifyToken(String token) { + try { + final Claims claims = getBody(token); + return true; + } catch (RuntimeException e) { + if (e instanceof ExpiredJwtException) { + throw new UnauthorizedException(Error.TOKEN_TIME_EXPIRED_EXCEPTION, Error.TOKEN_TIME_EXPIRED_EXCEPTION.getMessage()); + } + return false; + } + } + + private Claims getBody(final String token) { + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + // JWT 토큰 내용 확인 -> Long으로 형변환 필요 + public String getJwtContents(String token) { + final Claims claims = getBody(token); + return (String) claims.get("userId"); + } +} diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/resolver/UserId.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/resolver/UserId.java new file mode 100644 index 0000000..8569c2d --- /dev/null +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/resolver/UserId.java @@ -0,0 +1,11 @@ +package sopt.org.fourthSeminar.config.resolver; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) //어노테이션이 사용될 위치를 parameter로 지정 +@Retention(RetentionPolicy.RUNTIME) //컴파일 이후 런타임 동안 참조 가능 -> 어노테이션이 유효 +public @interface UserId { //@userId 생성 +} diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/resolver/UserIdResolver.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/resolver/UserIdResolver.java new file mode 100644 index 0000000..aa96ec4 --- /dev/null +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/config/resolver/UserIdResolver.java @@ -0,0 +1,51 @@ +package sopt.org.fourthSeminar.config.resolver; + +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import sopt.org.fourthSeminar.config.jwt.JwtService; +import sopt.org.fourthSeminar.exception.Error; +import sopt.org.fourthSeminar.exception.model.UnauthorizedException; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.constraints.NotNull; + +@RequiredArgsConstructor +@Component //타입기반의 자동주입 어노테이션 -> 등록된 Bean 객체 가져옴 + +//HandlerMethodArgumentResolver: 주어진 요청을 처리할 때, 메소드 파라미터를 인자값들에 주입 해주는 전략 인터페이스 +public class UserIdResolver implements HandlerMethodArgumentResolver { + + private final JwtService jwtService; + + //핸들러(컨트롤러 메소드)의 특정 파라미터를 지원하는지 여부를 판단하기 위한 메소드 + //어떤 파라미터에 대한 작업을 수행할 것인지 지정 + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(UserId.class) && Long.class.equals(parameter.getParameterType()); + } + + //파라미터에 대한 로직 수행 + @Override + public Object resolveArgument(@NotNull MethodParameter parameter, ModelAndViewContainer mavContainer, @NotNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + final HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + final String token = request.getHeader("Authorization"); + + //토큰 검증 + if (!jwtService.verifyToken(token)) { + throw new UnauthorizedException(Error.UNAUTHORIZED_TOKEN_EXCEPTION, Error.UNAUTHORIZED_TOKEN_EXCEPTION.getMessage()); + } + + //유저 아이디 반환 + final String tokenContents = jwtService.getJwtContents(token); + try { + return Long.parseLong(tokenContents); + } catch (NumberFormatException e) { + throw new RuntimeException(String.format("USER_ID를 가져오지 못했습니다. (%s - %s)", parameter.getClass(), parameter.getMethod())); + } + } +} diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/BoardController.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/BoardController.java index 234380b..2fa9451 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/BoardController.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/BoardController.java @@ -4,7 +4,9 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import sopt.org.fourthSeminar.common.dto.ApiResponse; -import sopt.org.fourthSeminar.controller.dto.BoardRequestDto; +import sopt.org.fourthSeminar.config.jwt.JwtService; +import sopt.org.fourthSeminar.config.resolver.UserId; +import sopt.org.fourthSeminar.controller.request.dto.BoardRequestDto; import sopt.org.fourthSeminar.exception.Success; import sopt.org.fourthSeminar.service.BoardService; @@ -16,11 +18,23 @@ public class BoardController { private final BoardService boardService; + private final JwtService jwtService; + +// @PostMapping("/create") +// @ResponseStatus(HttpStatus.CREATED) +// public ApiResponse create( +// @RequestHeader("Authorization") String accessToken, +// @RequestBody @Valid final BoardRequestDto request) { +// boardService.create(Long.parseLong(jwtService.getJwtContents(accessToken)), request); +// return ApiResponse.success(Success.CREATE_BOARD_SUCCESS); +// } @PostMapping("/create") @ResponseStatus(HttpStatus.CREATED) - public ApiResponse create(@RequestBody @Valid final BoardRequestDto request) { - boardService.create(request); + public ApiResponse create( + @UserId Long userId, + @RequestBody @Valid final BoardRequestDto request) { + boardService.create(userId, request); return ApiResponse.success(Success.CREATE_BOARD_SUCCESS); } diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/UserController.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/UserController.java index baa27e8..991ddab 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/UserController.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/UserController.java @@ -4,8 +4,11 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import sopt.org.fourthSeminar.common.dto.ApiResponse; -import sopt.org.fourthSeminar.controller.dto.UserRequestDto; -import sopt.org.fourthSeminar.controller.dto.UserResponseDto; +import sopt.org.fourthSeminar.config.jwt.JwtService; +import sopt.org.fourthSeminar.controller.request.dto.UserRequestDto; +import sopt.org.fourthSeminar.controller.response.dto.UserResponseDto; +import sopt.org.fourthSeminar.controller.request.dto.UserLoginRequestDto; +import sopt.org.fourthSeminar.controller.response.dto.UserLoginResponseDto; import sopt.org.fourthSeminar.exception.Success; import sopt.org.fourthSeminar.service.UserService; @@ -16,10 +19,19 @@ @RequestMapping("/user") public class UserController { private final UserService userService; + private final JwtService jwtService; @PostMapping("/signup") @ResponseStatus(HttpStatus.CREATED) public ApiResponse create(@RequestBody @Valid final UserRequestDto request) { return ApiResponse.success(Success.SIGNUP_SUCCESS, userService.create(request)); } + + @PostMapping("/login") + @ResponseStatus(HttpStatus.OK) + public ApiResponse login(@RequestBody @Valid final UserLoginRequestDto request) { + final Long userId = userService.login(request); + final String token = jwtService.issuedToken(String.valueOf(userId)); + return ApiResponse.success(Success.LOGIN_SUCCESS, UserLoginResponseDto.of(userId, token)); + } } diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/BoardRequestDto.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/BoardRequestDto.java similarity index 74% rename from fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/BoardRequestDto.java rename to fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/BoardRequestDto.java index 327438a..89e1f4c 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/BoardRequestDto.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/BoardRequestDto.java @@ -1,4 +1,4 @@ -package sopt.org.fourthSeminar.controller.dto; +package sopt.org.fourthSeminar.controller.request.dto; import lombok.AccessLevel; import lombok.Getter; @@ -12,8 +12,8 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class BoardRequestDto { - @Email(message = "이메일 형식에 맞지 않습니다") - private String email; +// @Email(message = "이메일 형식에 맞지 않습니다") +// private String email; @NotBlank private String title; diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/UserLoginRequestDto.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/UserLoginRequestDto.java new file mode 100644 index 0000000..0f96b52 --- /dev/null +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/UserLoginRequestDto.java @@ -0,0 +1,26 @@ +package sopt.org.fourthSeminar.controller.request.dto; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class UserLoginRequestDto { + + @Email(message = "이메일 형식에 맞지 않습니다") + @NotBlank + private String email; + + @NotNull + @Pattern( + regexp="(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,20}", + message = "비밀번호는 영문 대,소문자와 숫자, 특수기호가 적어도 1개 이상씩 포함된 8자 ~ 20자의 비밀번호여야 합니다" + ) + private String password; +} diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/UserRequestDto.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/UserRequestDto.java similarity index 94% rename from fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/UserRequestDto.java rename to fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/UserRequestDto.java index 95a89fb..2d2df09 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/UserRequestDto.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/request/dto/UserRequestDto.java @@ -1,4 +1,4 @@ -package sopt.org.fourthSeminar.controller.dto; +package sopt.org.fourthSeminar.controller.request.dto; import lombok.AccessLevel; import lombok.Getter; diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/response/dto/UserLoginResponseDto.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/response/dto/UserLoginResponseDto.java new file mode 100644 index 0000000..a0c299b --- /dev/null +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/response/dto/UserLoginResponseDto.java @@ -0,0 +1,18 @@ +package sopt.org.fourthSeminar.controller.response.dto; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class UserLoginResponseDto { + private Long userId; + private String accessToken; + + public static UserLoginResponseDto of(Long userId, String accessToken) { + return new UserLoginResponseDto(userId, accessToken); + } +} diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/UserResponseDto.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/response/dto/UserResponseDto.java similarity index 88% rename from fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/UserResponseDto.java rename to fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/response/dto/UserResponseDto.java index 32763b3..d034e3f 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/dto/UserResponseDto.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/controller/response/dto/UserResponseDto.java @@ -1,4 +1,4 @@ -package sopt.org.fourthSeminar.controller.dto; +package sopt.org.fourthSeminar.controller.response.dto; import lombok.AccessLevel; import lombok.AllArgsConstructor; diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/Error.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/Error.java index 1d4fedf..b195a42 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/Error.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/Error.java @@ -13,17 +13,29 @@ public enum Error { * 400 BAD REQUEST */ REQUEST_VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 요청입니다"), + NO_REQUEST_PARAMETER_EXCEPTION(HttpStatus.BAD_REQUEST, "요청 파라미터 값이 없습니다"), + VALIDATION_WRONG_TYPE_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 타입이 입력되었습니다"), + PARAMETER_TYPE_MISMATCH_EXCEPTION(HttpStatus.BAD_REQUEST, "파라미터의 타입이 잘못됐습니다"), + INVALID_PASSWORD_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 비밀번호가 입력됐습니다."), + + /** + * 401 UNAUTHORIZED + */ + UNAUTHORIZED_TOKEN_EXCEPTION(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), + TOKEN_TIME_EXPIRED_EXCEPTION(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."), /** * 404 NOT FOUND */ NOT_FOUND_USER_EXCEPTION(HttpStatus.NOT_FOUND, "존재하지 않는 유저입니다"), + NOT_FOUND_POST_EXCEPTION(HttpStatus.NOT_FOUND, "존재하지 않는 게시물입니다"), + NOT_FOUND_EMOTION_EXCEPTION(HttpStatus.NOT_FOUND, "존재하지 않는 감정 기록입니다"), /** * 409 CONFLICT */ ALREADY_EXIST_USER_EXCEPTION(HttpStatus.CONFLICT, "이미 존재하는 유저입니다"), - + ALREADY_EXIST_EMOTION_EXCEPTION(HttpStatus.CONFLICT, "이미 해당 날짜에 감정이 기록되었습니다"), /** * 500 INTERNAL SERVER ERROR diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/Success.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/Success.java index bef213f..4a30f6f 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/Success.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/Success.java @@ -8,11 +8,22 @@ @Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) public enum Success { + + /** + * 200 OK + */ + LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), + GET_POST_LIST_SUCCESS(HttpStatus.OK, "게시물 리스트 조회에 성공했습니다."), + GET_POST_SUCCESS(HttpStatus.OK, "게시물 조회에 성공했습니다."), + GET_EMOTION_CALENDAR_SUCCESS(HttpStatus.OK, "감정 캘린더 조회에 성공했습니다."), + GET_EMOTION_SUCCESS(HttpStatus.OK, "감정 조회에 성공했습니다."), + /** * 201 CREATED */ SIGNUP_SUCCESS(HttpStatus.CREATED, "회원가입이 완료됐습니다."), CREATE_BOARD_SUCCESS(HttpStatus.CREATED, "게시물 생성이 완료됐습니다."), + CREATE_EMOTION_SUCCESS(HttpStatus.CREATED, "감정 기록에 성공했습니다."), ; private final HttpStatus httpStatus; diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/BadRequestException.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/BadRequestException.java new file mode 100644 index 0000000..894dbaa --- /dev/null +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/BadRequestException.java @@ -0,0 +1,12 @@ +package sopt.org.fourthSeminar.exception.model; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; +import sopt.org.fourthSeminar.exception.Error; + +//@ResponseStatus(HttpStatus.BAD_REQUEST) +public class BadRequestException extends SoptException{ + public BadRequestException(Error error, String message) { + super(error, message); + } +} diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/NotFoundException.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/NotFoundException.java index 7851a59..61f3b4c 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/NotFoundException.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/NotFoundException.java @@ -1,7 +1,10 @@ package sopt.org.fourthSeminar.exception.model; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; import sopt.org.fourthSeminar.exception.Error; +@ResponseStatus(HttpStatus.NOT_FOUND) public class NotFoundException extends SoptException{ public NotFoundException(Error error, String message) { diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/UnauthorizedException.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/UnauthorizedException.java new file mode 100644 index 0000000..e1e697f --- /dev/null +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/exception/model/UnauthorizedException.java @@ -0,0 +1,9 @@ +package sopt.org.fourthSeminar.exception.model; + +import sopt.org.fourthSeminar.exception.Error; + +public class UnauthorizedException extends SoptException{ + public UnauthorizedException(Error error, String message) { + super(error, message); + } +} diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/infrastructure/UserRepository.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/infrastructure/UserRepository.java index e987abb..645da9f 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/infrastructure/UserRepository.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/infrastructure/UserRepository.java @@ -10,6 +10,7 @@ public interface UserRepository extends Repository { void save(User user); //READ + Optional findById(Long id); Optional findByEmail(String email); boolean existsByEmail(String email); diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/service/BoardService.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/service/BoardService.java index d489243..e0559a0 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/service/BoardService.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/service/BoardService.java @@ -2,7 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import sopt.org.fourthSeminar.controller.dto.BoardRequestDto; +import sopt.org.fourthSeminar.controller.request.dto.BoardRequestDto; import sopt.org.fourthSeminar.domain.Board; import sopt.org.fourthSeminar.domain.User; import sopt.org.fourthSeminar.exception.Error; @@ -19,8 +19,8 @@ public class BoardService { private final BoardRepository boardRepository; @Transactional - public void create(BoardRequestDto request) { - User user = userRepository.findByEmail(request.getEmail()) + public void create(Long userId, BoardRequestDto request) { + User user = userRepository.findById(userId) .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); Board newBoard = Board.newInstance( diff --git a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/service/UserService.java b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/service/UserService.java index 08b2de7..f670814 100644 --- a/fourthSeminar/src/main/java/sopt/org/fourthSeminar/service/UserService.java +++ b/fourthSeminar/src/main/java/sopt/org/fourthSeminar/service/UserService.java @@ -3,11 +3,14 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import sopt.org.fourthSeminar.controller.dto.UserRequestDto; -import sopt.org.fourthSeminar.controller.dto.UserResponseDto; +import sopt.org.fourthSeminar.controller.request.dto.UserRequestDto; +import sopt.org.fourthSeminar.controller.response.dto.UserResponseDto; +import sopt.org.fourthSeminar.controller.request.dto.UserLoginRequestDto; import sopt.org.fourthSeminar.domain.User; import sopt.org.fourthSeminar.exception.Error; +import sopt.org.fourthSeminar.exception.model.BadRequestException; import sopt.org.fourthSeminar.exception.model.ConflictException; +import sopt.org.fourthSeminar.exception.model.NotFoundException; import sopt.org.fourthSeminar.infrastructure.UserRepository; @Service @@ -32,4 +35,16 @@ public UserResponseDto create(UserRequestDto request) { return UserResponseDto.of(newUser.getId(), newUser.getNickname()); } + @Transactional + public Long login(final UserLoginRequestDto request) { + User user = userRepository.findByEmail(request.getEmail()) + .orElseThrow(() -> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage())); + + if(!user.getPassword().equals(request.getPassword())) { + throw new BadRequestException(Error.INVALID_PASSWORD_EXCEPTION, Error.INVALID_PASSWORD_EXCEPTION.getMessage()); + } + + return user.getId(); + } + }