diff --git a/src/main/java/com/s1350/sooljangmacha/global/dto/BaseResponse.java b/src/main/java/com/s1350/sooljangmacha/global/dto/BaseResponse.java index 6029b12..401fd91 100644 --- a/src/main/java/com/s1350/sooljangmacha/global/dto/BaseResponse.java +++ b/src/main/java/com/s1350/sooljangmacha/global/dto/BaseResponse.java @@ -30,7 +30,7 @@ public static BaseResponse OK(@Nullable T data) { } public static BaseResponse error(BaseResponseCode baseResponseCode, String message) { - return new BaseResponse<>(baseResponseCode.getStatus().value(), baseResponseCode.getCode(), baseResponseCode.getMessage()); + return new BaseResponse<>(baseResponseCode.getStatus().value(), baseResponseCode.getCode(), message); } } diff --git a/src/main/java/com/s1350/sooljangmacha/global/utils/JwtUtil.java b/src/main/java/com/s1350/sooljangmacha/global/utils/JwtUtil.java index 7ec9fcf..9cf12f9 100644 --- a/src/main/java/com/s1350/sooljangmacha/global/utils/JwtUtil.java +++ b/src/main/java/com/s1350/sooljangmacha/global/utils/JwtUtil.java @@ -9,6 +9,7 @@ import java.nio.charset.StandardCharsets; import java.security.Key; +import java.util.Date; import static com.s1350.sooljangmacha.global.Constants.BEARER_PREFIX; import static com.s1350.sooljangmacha.global.Constants.CLAIM_NAME; @@ -19,6 +20,8 @@ public class JwtUtil { @Value("${jwt.secret}") private String jwtSecret; + private final long accessTokenExpiryTime = 1000L * 60 * 60 * 24 * 14; // 2주 + public static String replaceBearer(String header) { return header.substring(BEARER_PREFIX.length()); } @@ -57,4 +60,25 @@ private Claims getBody(String token) { return e.getClaims(); } } + + public String issuedAccessToken(Long userId) { + return issuedToken("access_token", accessTokenExpiryTime, String.valueOf(userId)); + } + + public String issuedToken(String tokenName, long expiryTime, String userId) { + final Date now = new Date(); + + final Claims claims = Jwts.claims() + .setSubject(tokenName) + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + expiryTime)); + + claims.put(CLAIM_NAME, userId); + + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) + .setClaims(claims) + .signWith(getSigningKey()) + .compact(); + } } diff --git a/src/main/java/com/s1350/sooljangmacha/user/controller/UserController.java b/src/main/java/com/s1350/sooljangmacha/user/controller/UserController.java index 30b8b59..3d65f4a 100644 --- a/src/main/java/com/s1350/sooljangmacha/user/controller/UserController.java +++ b/src/main/java/com/s1350/sooljangmacha/user/controller/UserController.java @@ -1,10 +1,23 @@ package com.s1350.sooljangmacha.user.controller; +import com.s1350.sooljangmacha.global.dto.BaseResponse; +import com.s1350.sooljangmacha.user.dto.request.LoginReq; +import com.s1350.sooljangmacha.user.dto.response.LoginRes; import com.s1350.sooljangmacha.user.service.UserService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; @Tag(name = "users", description = "유저 API") @RestController @@ -13,8 +26,17 @@ public class UserController { private final UserService userService; -// @Operation(summary = "로그인", description = "") -// @PostMapping("/login") + @Operation(summary = "로그인", description = "로그인을 한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "(S0001)요청에 성공했습니다."), + @ApiResponse(responseCode = "400", description = "(E0001)잘못된 요청입니다.
(E0001)이메일을 입력해 주세요.
(E0001)provider를 입력해 주세요.
(E0001)올바른 이메일 형식으로 입력해 주세요.
(E0001)잘못된 provider 값 입니다.
", content = @Content(schema = @Schema(implementation = BaseResponse.class))), + @ApiResponse(responseCode = "404", description = "(U0001)존재하지 않는 유저입니다.", content = @Content(schema = @Schema(implementation = BaseResponse.class))), + }) + @PostMapping("/login") + public BaseResponse login(@RequestBody @Valid LoginReq loginReq) { + return BaseResponse.OK(userService.login(loginReq)); + } + // @Operation(summary = "로그아웃", description = "") // @PostMapping("/logout") diff --git a/src/main/java/com/s1350/sooljangmacha/user/dto/request/LoginReq.java b/src/main/java/com/s1350/sooljangmacha/user/dto/request/LoginReq.java new file mode 100644 index 0000000..04913dc --- /dev/null +++ b/src/main/java/com/s1350/sooljangmacha/user/dto/request/LoginReq.java @@ -0,0 +1,26 @@ +package com.s1350.sooljangmacha.user.dto.request; + +import com.s1350.sooljangmacha.global.resolver.EnumValid; +import com.s1350.sooljangmacha.user.entity.Provider; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; + +@Data +@NoArgsConstructor +public class LoginReq { + + @Schema(type = "String", description = "유저 이메일", example = "ex@naver.com", required = true) + @NotBlank(message = "이메일을 입력해 주세요.") + @Email(message = "올바른 이메일 형식으로 입력해 주세요.") + private String email; + + @Schema(type = "String", description = "유저 로그인 종류", example = "KAKAO", allowableValues = {"KAKAO", "APPLE"}, required = true) + @NotBlank(message = "provider를 입력해 주세요.") + @EnumValid(enumClass = Provider.class, ignoreCase = true, message = "잘못된 provider 값 입니다.") + private String provider; + +} diff --git a/src/main/java/com/s1350/sooljangmacha/user/dto/response/LoginRes.java b/src/main/java/com/s1350/sooljangmacha/user/dto/response/LoginRes.java new file mode 100644 index 0000000..0793d6f --- /dev/null +++ b/src/main/java/com/s1350/sooljangmacha/user/dto/response/LoginRes.java @@ -0,0 +1,23 @@ +package com.s1350.sooljangmacha.user.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LoginRes { + + @Schema(type = "String", description = "액세스 토큰") + private String accessToken; + + public static LoginRes toEntity(String accessToken) { + return LoginRes.builder() + .accessToken(accessToken) + .build(); + } +} diff --git a/src/main/java/com/s1350/sooljangmacha/user/entity/User.java b/src/main/java/com/s1350/sooljangmacha/user/entity/User.java index 851133f..b0bf552 100644 --- a/src/main/java/com/s1350/sooljangmacha/user/entity/User.java +++ b/src/main/java/com/s1350/sooljangmacha/user/entity/User.java @@ -25,6 +25,10 @@ public class User extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @NotNull + @Size(max = 255) + private String email; + @NotNull @Size(max = 30) private String nickname; diff --git a/src/main/java/com/s1350/sooljangmacha/user/repository/UserRepository.java b/src/main/java/com/s1350/sooljangmacha/user/repository/UserRepository.java index b6c1924..89f81a6 100644 --- a/src/main/java/com/s1350/sooljangmacha/user/repository/UserRepository.java +++ b/src/main/java/com/s1350/sooljangmacha/user/repository/UserRepository.java @@ -1,5 +1,6 @@ package com.s1350.sooljangmacha.user.repository; +import com.s1350.sooljangmacha.user.entity.Provider; import com.s1350.sooljangmacha.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; @@ -7,4 +8,6 @@ public interface UserRepository extends JpaRepository { Optional findByIdAndIsEnable(Long id, boolean isEnable); + + Optional findByEmailAndProviderAndIsEnable(String email, Provider provider, boolean isEnable); } diff --git a/src/main/java/com/s1350/sooljangmacha/user/service/UserService.java b/src/main/java/com/s1350/sooljangmacha/user/service/UserService.java index 35ab43b..480ecdf 100644 --- a/src/main/java/com/s1350/sooljangmacha/user/service/UserService.java +++ b/src/main/java/com/s1350/sooljangmacha/user/service/UserService.java @@ -1,16 +1,30 @@ package com.s1350.sooljangmacha.user.service; +import com.s1350.sooljangmacha.global.exception.BaseException; +import com.s1350.sooljangmacha.global.exception.BaseResponseCode; +import com.s1350.sooljangmacha.global.utils.JwtUtil; +import com.s1350.sooljangmacha.user.dto.request.LoginReq; +import com.s1350.sooljangmacha.user.dto.response.LoginRes; +import com.s1350.sooljangmacha.user.entity.Provider; +import com.s1350.sooljangmacha.user.entity.User; import com.s1350.sooljangmacha.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; + @Service @RequiredArgsConstructor public class UserService { - private UserRepository userRepository; + private final UserRepository userRepository; + private final JwtUtil jwtUtil; // 로그인 + public LoginRes login(LoginReq request) { + User user = userRepository.findByEmailAndProviderAndIsEnable(request.getEmail(), Provider.valueOf(request.getProvider()), true) + .orElseThrow(() -> new BaseException(BaseResponseCode.USER_NOT_FOUND)); + return LoginRes.toEntity(jwtUtil.issuedAccessToken(user.getId())); + } // 로그아웃