Skip to content

Commit

Permalink
Merge pull request #54 from backendoori/feature-user-validation
Browse files Browse the repository at this point in the history
🚀 사용자 인증 요청 및 user entity validation 추가
  • Loading branch information
ASak1104 authored Jan 8, 2024
2 parents d2677a5 + 5bb4948 commit dba9cc0
Show file tree
Hide file tree
Showing 19 changed files with 555 additions and 116 deletions.
33 changes: 33 additions & 0 deletions src/main/java/com/backendoori/ootw/common/AssertUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.backendoori.ootw.common;

import java.util.Objects;
import java.util.function.Supplier;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class AssertUtil extends Assert {

public static void notBlank(@Nullable String string, String message) {
if (string == null || string.isBlank()) {
throw new IllegalArgumentException(message);
}
}

public static void hasPattern(@Nullable String string, @Nullable String pattern, String message) {
notNull(string, message);

if (!string.matches(Objects.requireNonNull(pattern))) {
throw new IllegalArgumentException(message);
}
}

public static void throwIf(boolean state, Supplier<RuntimeException> exceptionSupplier) {
if (state) {
throw exceptionSupplier.get();
}
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import java.util.List;
import java.util.NoSuchElementException;
import com.backendoori.ootw.exception.ExceptionResponse.FieldErrorDetail;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
Expand All @@ -17,27 +19,28 @@
@RestControllerAdvice
public class GlobalControllerAdvice {

@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<ErrorResponse> handleAuthenticationException(AuthenticationException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());

return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(errorResponse);
}
public static final String DEFAULT_MESSAGE = "유효하지 않은 요청 입니다.";

@ExceptionHandler(NoSuchElementException.class)
public ResponseEntity<ErrorResponse> handleNoSuchElementException(NoSuchElementException e) {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());

return ResponseEntity.status(HttpStatus.NOT_FOUND)
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(errorResponse);
}

@ExceptionHandler(DuplicateKeyException.class)
public ResponseEntity<ErrorResponse> handleDuplicateKeyException(DuplicateKeyException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
List<FieldError> errors = e.getFieldErrors();
String message = errors.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.filter(Objects::nonNull)
.findFirst()
.orElse(DEFAULT_MESSAGE);

return ResponseEntity.status(HttpStatus.CONFLICT)
ErrorResponse errorResponse = new ErrorResponse(message);

return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(errorResponse);
}

Expand All @@ -54,23 +57,36 @@ public ResponseEntity<ErrorResponse> handlerMethodValidationException(HandlerMet
.body(errorResponse);
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ExceptionResponse<List<FieldErrorDetail>>> handleMethodArgumentNotValidException(
MethodArgumentNotValidException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ExceptionResponse.from(e));
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<ErrorResponse> handleAuthenticationException(AuthenticationException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());

return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(errorResponse);
}

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ExceptionResponse<String>> handleIllegalArgumentException(IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ExceptionResponse.from(e));
@ExceptionHandler(NoSuchElementException.class)
public ResponseEntity<ErrorResponse> handleNoSuchElementException(NoSuchElementException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());

return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(errorResponse);
}

@ExceptionHandler(DuplicateKeyException.class)
public ResponseEntity<ErrorResponse> handleDuplicateKeyException(DuplicateKeyException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());

return ResponseEntity.status(HttpStatus.CONFLICT)
.body(errorResponse);
}

@ExceptionHandler(ImageUploadException.class)
public ResponseEntity<ExceptionResponse<String>> handleImageUploadException(ImageUploadException e) {
public ResponseEntity<ErrorResponse> handleImageUploadException(ImageUploadException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage());

return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(ExceptionResponse.from(e));
.body(errorResponse);
}

@ExceptionHandler(Exception.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.backendoori.ootw.user.dto.TokenDto;
import com.backendoori.ootw.user.dto.UserDto;
import com.backendoori.ootw.user.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
Expand All @@ -23,15 +24,15 @@ public class UserController {
private final UserService userService;

@PostMapping("/signup")
public ResponseEntity<UserDto> signup(@RequestBody SignupDto signupDto) {
public ResponseEntity<UserDto> signup(@RequestBody @Valid SignupDto signupDto) {
UserDto userDto = userService.signup(signupDto);

return ResponseEntity.status(HttpStatus.CREATED)
.body(userDto);
}

@PostMapping("/login")
public ResponseEntity<TokenDto> login(@RequestBody LoginDto loginDto) {
public ResponseEntity<TokenDto> login(@RequestBody @Valid LoginDto loginDto) {
TokenDto tokenDto = userService.login(loginDto);
HttpHeaders httpHeaders = new HttpHeaders();

Expand Down
17 changes: 15 additions & 2 deletions src/main/java/com/backendoori/ootw/user/domain/User.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package com.backendoori.ootw.user.domain;

import com.backendoori.ootw.common.AssertUtil;
import com.backendoori.ootw.common.BaseEntity;
import com.backendoori.ootw.user.validation.Message;
import com.backendoori.ootw.user.validation.RFC5322;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -18,7 +20,6 @@
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class User extends BaseEntity {

@Id
Expand All @@ -38,4 +39,16 @@ public class User extends BaseEntity {
@Column(name = "image")
private String image;

public User(Long id, String email, String password, String nickname, String image) {
AssertUtil.hasPattern(email, RFC5322.REGEX, Message.INVALID_EMAIL);
AssertUtil.notBlank(password, Message.BLANK_PASSWORD);
AssertUtil.notBlank(nickname, Message.BLANK_NICKNAME);

this.id = id;
this.email = email;
this.password = password;
this.nickname = nickname;
this.image = image;
}

}
12 changes: 12 additions & 0 deletions src/main/java/com/backendoori/ootw/user/dto/LoginDto.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
package com.backendoori.ootw.user.dto;

import com.backendoori.ootw.user.validation.Password;
import com.backendoori.ootw.user.validation.RFC5322;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record LoginDto(
@NotNull
@NotBlank
@Email(regexp = RFC5322.REGEX)
String email,

@NotNull
@Password
String password
) {

Expand Down
19 changes: 17 additions & 2 deletions src/main/java/com/backendoori/ootw/user/dto/SignupDto.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
package com.backendoori.ootw.user.dto;

import com.backendoori.ootw.user.validation.Message;
import com.backendoori.ootw.user.validation.Password;
import com.backendoori.ootw.user.validation.RFC5322;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record SignupDto(
@NotNull
@NotBlank
@Email(regexp = RFC5322.REGEX)
String email,

@NotNull
@Password
String password,
String nickname,
String image

@NotNull
@NotBlank(message = Message.BLANK_NICKNAME)
String nickname
) {

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backendoori.ootw.exception;
package com.backendoori.ootw.user.exception;

import org.springframework.dao.DuplicateKeyException;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backendoori.ootw.exception;
package com.backendoori.ootw.user.exception;

import org.springframework.security.core.AuthenticationException;

Expand Down
41 changes: 27 additions & 14 deletions src/main/java/com/backendoori/ootw/user/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package com.backendoori.ootw.user.service;

import com.backendoori.ootw.exception.AlreadyExistEmailException;
import com.backendoori.ootw.exception.IncorrectPasswordException;
import com.backendoori.ootw.common.AssertUtil;
import com.backendoori.ootw.exception.UserNotFoundException;
import com.backendoori.ootw.security.jwt.TokenProvider;
import com.backendoori.ootw.user.domain.User;
import com.backendoori.ootw.user.dto.LoginDto;
import com.backendoori.ootw.user.dto.SignupDto;
import com.backendoori.ootw.user.dto.TokenDto;
import com.backendoori.ootw.user.dto.UserDto;
import com.backendoori.ootw.user.exception.AlreadyExistEmailException;
import com.backendoori.ootw.user.exception.IncorrectPasswordException;
import com.backendoori.ootw.user.repository.UserRepository;
import com.backendoori.ootw.user.validation.Message;
import com.backendoori.ootw.user.validation.Password;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Service
@RequiredArgsConstructor
Expand All @@ -28,16 +32,10 @@ public UserDto signup(SignupDto signupDto) {
boolean isAlreadyExistEmail = userRepository.findByEmail(signupDto.email())
.isPresent();

if (isAlreadyExistEmail) {
throw new AlreadyExistEmailException();
}
AssertUtil.throwIf(isAlreadyExistEmail, AlreadyExistEmailException::new);
AssertUtil.isTrue(isValidPassword(signupDto.password()), Message.INVALID_PASSWORD);

User user = User.builder()
.email(signupDto.email())
.password(passwordEncoder.encode(signupDto.password()))
.nickname(signupDto.nickname())
.image(signupDto.image())
.build();
User user = buildUser(signupDto);

userRepository.save(user);

Expand All @@ -47,14 +45,29 @@ public UserDto signup(SignupDto signupDto) {
public TokenDto login(LoginDto loginDto) {
User user = userRepository.findByEmail(loginDto.email())
.orElseThrow(UserNotFoundException::new);
boolean isIncorrectPassword = !matchPassword(loginDto.password(), user.getPassword());

if (!passwordEncoder.matches(loginDto.password(), user.getPassword())) {
throw new IncorrectPasswordException();
}
AssertUtil.throwIf(isIncorrectPassword, IncorrectPasswordException::new);

String token = tokenProvider.createToken(user.getId());

return new TokenDto(token);
}

private User buildUser(SignupDto signupDto) {
return User.builder()
.email(signupDto.email())
.password(passwordEncoder.encode(signupDto.password()))
.nickname(signupDto.nickname())
.build();
}

private boolean matchPassword(String decrypted, String encrypted) {
return passwordEncoder.matches(decrypted, encrypted);
}

private boolean isValidPassword(String password) {
return StringUtils.hasLength(password) && password.matches(Password.REGEX);
}

}
Loading

0 comments on commit dba9cc0

Please sign in to comment.