diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/controller/admin/AdminController.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/controller/admin/AdminController.java index 1e2ae76..ba9a83a 100644 --- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/controller/admin/AdminController.java +++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/controller/admin/AdminController.java @@ -16,23 +16,24 @@ @RequiredArgsConstructor @RestController public class AdminController { + private final AuthService authService; private final ChallengeGroupService challengeGroupService; private final BCryptPasswordEncoder passwordEncoder; @ResponseStatus(HttpStatus.CREATED) - @Operation(summary= "매니저 등록", description = "매니저를 등록한다.") + @Operation(summary = "매니저 등록", description = "매니저를 등록한다.") @PostMapping("/api/admin/auth/manager") public ApiResponse createManager(@RequestBody @Valid AuthReq.EmailSignupRequest request) { - authService.createManager(request.toCommand() - .changePassword(passwordEncoder.encode(request.password()))); + authService.createManager(request.toCommand()); return ApiResponse.success(null, "매니저 등록 성공"); } @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "챌린지 그룹 생성", description = "챌린지 그룹과 해당하는 챌린지를 생성합니다") @PostMapping("/api/admin/challengeGroups") - public ApiResponse createChallengeGroup(@RequestBody @Valid AdminReq.CreateChallengeGroupRequest request) { + public ApiResponse createChallengeGroup( + @RequestBody @Valid AdminReq.CreateChallengeGroupRequest request) { challengeGroupService.createChallengeGroup(request.toCommand()); return ApiResponse.success(null, "챌린지 그룹 생성 성공"); } diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/controller/auth/AuthController.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/controller/auth/AuthController.java index d489fe6..a34f434 100644 --- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/controller/auth/AuthController.java +++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/controller/auth/AuthController.java @@ -19,31 +19,33 @@ @RequiredArgsConstructor @RestController public class AuthController { + private final AuthService authService; private final BCryptPasswordEncoder passwordEncoder; @Operation(summary = "oauth2 로그인", description = "oauth2 code를 이용하여 로그인한다.") @PostMapping("/api/auth/oauth2") - public ApiResponse oauth2(@RequestBody @Valid AuthReq.OAuth2LoginRequest request) { - Pair pair = authService.oAuth2LoginOrSignup(request.provider(), request.code(), request.state()); + public ApiResponse oauth2( + @RequestBody @Valid AuthReq.OAuth2LoginRequest request) { + Pair pair = authService.oAuth2LoginOrSignup(request.provider(), + request.code(), request.state()); var response = AuthRes.LoginResponse.from(pair.getFirst(), pair.getSecond()); return ApiResponse.success(response); } @Operation(summary = "이메일 회원가입", description = "이메일 회원가입을 한다.") @PostMapping("/api/auth/signup") - public ApiResponse signup(@RequestBody @Valid AuthReq.EmailSignupRequest request) { - Pair pair = authService.signup( - request.toCommand() - .changePassword(passwordEncoder.encode(request.password())) - ); + public ApiResponse signup( + @RequestBody @Valid AuthReq.EmailSignupRequest request) { + Pair pair = authService.signup(request.toCommand()); var response = AuthRes.LoginResponse.from(pair.getFirst(), pair.getSecond()); return ApiResponse.success(response); } @Operation(summary = "로그인", description = "로그인한다.") @PostMapping("/api/auth/login") - public ApiResponse login(@RequestBody @Valid AuthReq.EmailLoginRequest request) { + public ApiResponse login( + @RequestBody @Valid AuthReq.EmailLoginRequest request) { Pair pair = authService.login(request.email(), request.password()); var response = AuthRes.LoginResponse.from(pair.getFirst(), pair.getSecond()); return ApiResponse.success(response); @@ -52,9 +54,9 @@ public ApiResponse login(@RequestBody @Valid AuthReq.Emai @Operation(summary = "액세스 토큰 재발급", description = "리프레시 토큰을 이용하여 액세스 토큰을 재발급한다.") @PostMapping("/api/auth/refresh") public ApiResponse refresh( - @RequestHeader("Authorization") String authorization + @RequestHeader("Authorization") String authorization ) { - if(authorization == null || !authorization.startsWith("Bearer ")) { + if (authorization == null || !authorization.startsWith("Bearer ")) { throw new IllegalArgumentException("Bearer 토큰이 필요합니다."); } String rawToken = authorization.substring("Bearer ".length()); diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/auth/AuthService.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/auth/AuthService.java index 1b072ac..c3b1a4b 100644 --- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/auth/AuthService.java +++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/auth/AuthService.java @@ -17,6 +17,7 @@ @Service @RequiredArgsConstructor public class AuthService { + private final List oAuth2Clients; private final BCryptPasswordEncoder passwordEncoder; private final JwtUtils jwtUtils; @@ -24,25 +25,23 @@ public class AuthService { private final UserStore userStore; /** - * OAuth2 로그인 또는 회원가입
- * [state]는 nullable한 입력 값이다.
- * 1. OAuth2Client를 이용해 해당 provider로부터 유저정보를 가져옴 - * 2. authToken으로 유저를 찾거나 없으면 회원가입 - * 3. 토큰 발급, 유저정보 반환 + * OAuth2 로그인 또는 회원가입
[state]는 nullable한 입력 값이다.
1. OAuth2Client를 이용해 해당 provider로부터 + * 유저정보를 가져옴 2. authToken으로 유저를 찾거나 없으면 회원가입 3. 토큰 발급, 유저정보 반환 */ - public Pair oAuth2LoginOrSignup(OAuth2Provider provider, @NonNull String code, @Nullable String state) { + public Pair oAuth2LoginOrSignup(OAuth2Provider provider, + @NonNull String code, @Nullable String state) { OAuth2Client oAuth2Client = oAuth2Clients.stream() - .filter(client -> client.canHandle(provider)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("지원하지 않는 OAuth2Provider 입니다.")); + .filter(client -> client.canHandle(provider)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("지원하지 않는 OAuth2Provider 입니다.")); // OAuth2Client를 이용해 해당 provider로부터 유저정보를 가져옴 OAuthUserInfoModel oAuthUserInfoModel = oAuth2Client.getAuthToken(code, state); // authToken으로 유저를 찾아서 없으면 [OAuthUserInfoModel]를 통해서 회원가입 진행 User user = userReader - .findByAuthToken(oAuthUserInfoModel.authToken()) - .orElseGet(() -> signup(oAuthUserInfoModel, provider)); + .findByAuthToken(oAuthUserInfoModel.authToken()) + .orElseGet(() -> signup(oAuthUserInfoModel, provider)); // 토큰 발급, 유저정보 반환 JwtToken jwtToken = createToken(user); @@ -53,7 +52,7 @@ public Pair oAuth2LoginOrSignup(OAuth2Provider provider, @N private User signup(OAuthUserInfoModel oAuthUserInfoModel, OAuth2Provider provider) { UserCommand.CreateOAuth2 command = oAuthUserInfoModel.toCreateCommand(provider); - User user = User.create(command); + User user = User.create(command); return userStore.store(user); } @@ -63,11 +62,11 @@ private JwtToken createToken(User user) { } @Transactional - public Pair signup(UserCommand.Create command){ - if(userReader.existsByEmail(command.getEmail())){ + public Pair signup(UserCommand.Create command) { + if (userReader.existsByEmail(command.getEmail())) { throw new IllegalArgumentException("이미 존재하는 이메일입니다."); } - + command = command.copyEncodedPassword(passwordEncoder.encode(command.getPassword())); User user = User.create(command); userStore.store(user); JwtToken jwtToken = createToken(user); @@ -76,11 +75,11 @@ public Pair signup(UserCommand.Create command){ } @Transactional - public void createManager(UserCommand.Create command){ - if(userReader.existsByEmail(command.getEmail())){ + public void createManager(UserCommand.Create command) { + if (userReader.existsByEmail(command.getEmail())) { throw new IllegalArgumentException("이미 존재하는 이메일입니다."); } - + command = command.copyEncodedPassword(passwordEncoder.encode(command.getPassword())); User user = User.createManager(command); userStore.store(user); } @@ -88,9 +87,9 @@ public void createManager(UserCommand.Create command){ @Transactional(readOnly = true) public Pair login(String email, String password) { User user = userReader.findByEmail(email) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 이메일입니다.")); + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 이메일입니다.")); - if(!passwordEncoder.matches(password, user.getPassword())){ + if (!passwordEncoder.matches(password, user.getPassword())) { throw new IllegalArgumentException("비밀번호가 일치하지 않습니다."); } @@ -100,7 +99,7 @@ public Pair login(String email, String password) { } public String reissueToken(String rawToken) { - if(!jwtUtils.validateToken(rawToken)){ + if (!jwtUtils.validateToken(rawToken)) { throw new IllegalArgumentException("RefreshToken이 유효하지 않습니다."); } JwtToken.ValidToken token = JwtToken.ValidToken.of(rawToken); diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/user/User.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/user/User.java index 09efff2..ffb6b5f 100644 --- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/user/User.java +++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/user/User.java @@ -13,6 +13,7 @@ @AllArgsConstructor @Table(name = "users") public class User extends BaseTimeEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -40,48 +41,54 @@ public class User extends BaseTimeEntity { public static User create(UserCommand.CreateOAuth2 command) { return User.builder() - .nickname(command.getNickname()) - .email(null) - .password(null) - .role(Role.USER) - .provider(command.getProvider()) - .authToken(command.getAuthToken()) - .exp(0) - .profileImageUrl(null) - .build(); + .nickname(command.getNickname()) + .email(null) + .password(null) + .role(Role.USER) + .provider(command.getProvider()) + .authToken(command.getAuthToken()) + .exp(0) + .profileImageUrl(null) + .build(); } - public static User create(UserCommand.Create command){ + public static User create(UserCommand.Create command) { + if (!command.isValid()) { + throw new IllegalStateException("인코딩에 실패하였습니다."); + } return User.builder() - .nickname(command.getNickname()) - .email(command.getEmail()) - .password(command.getPassword()) - .role(Role.USER) - .provider(null) - .authToken(null) - .exp(0) - .profileImageUrl(null) - .build(); + .nickname(command.getNickname()) + .email(command.getEmail()) + .password(command.getPassword()) + .role(Role.USER) + .provider(null) + .authToken(null) + .exp(0) + .profileImageUrl(null) + .build(); } - public static User createManager(UserCommand.Create command){ + public static User createManager(UserCommand.Create command) { + if (!command.isValid()) { + throw new IllegalStateException("인코딩에 실패하였습니다."); + } return User.builder() - .nickname(command.getNickname()) - .email(command.getEmail()) - .password(command.getPassword()) - .role(Role.MANAGER) - .provider(null) - .authToken(null) - .exp(0) - .profileImageUrl(null) - .build(); + .nickname(command.getNickname()) + .email(command.getEmail()) + .password(command.getPassword()) + .role(Role.MANAGER) + .provider(null) + .authToken(null) + .exp(0) + .profileImageUrl(null) + .build(); } public void update(UserCommand.Update userUpdate) { this.nickname = userUpdate.getNickname(); } - public void addExp(Integer exp){ + public void addExp(Integer exp) { this.exp += exp; } } diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/user/UserCommand.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/user/UserCommand.java index 5121025..72cd1ad 100644 --- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/user/UserCommand.java +++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/domain/user/UserCommand.java @@ -9,31 +9,50 @@ import org.haedal.zzansuni.domain.auth.OAuth2Provider; public class UserCommand { + @Getter public static class Create extends SelfValidating { + @Email(message = "이메일 형식은 유효하여야 합니다.") private final String email; @NotBlank(message = "password는 필수입니다.") - private String password; + private final String password; @NotBlank(message = "닉네임은 필수입니다.") private final String nickname; + private final String encodedPassword; + @Builder public Create(String email, String password, String nickname) { this.email = email; this.password = password; this.nickname = nickname; + this.encodedPassword = null; + this.validateSelf(); } - public UserCommand.Create changePassword(String encodedPassword){ - this.password = encodedPassword; - return this; + private Create(String email, String password, String nickname, String encodedPassword) { + this.email = email; + this.password = password; + this.nickname = nickname; + this.encodedPassword = encodedPassword; } + + public UserCommand.Create copyEncodedPassword(String encodedPassword) { + return new Create(email, null, nickname, encodedPassword); + } + + public boolean isValid() { + return password == null && encodedPassword != null; + } + + } @Getter public static class CreateOAuth2 extends SelfValidating { + private final String authToken; private final String nickname; @NotNull(message = "OAuth2Provider는 필수입니다.") @@ -44,7 +63,7 @@ public CreateOAuth2(String authToken, String nickname, OAuth2Provider provider) this.authToken = authToken; this.nickname = nickname; this.provider = provider; - if(authToken.isBlank() || nickname.isBlank()){ + if (authToken.isBlank() || nickname.isBlank()) { throw new RuntimeException("authToken, nickname은 필수입니다."); } this.validateSelf(); @@ -53,6 +72,7 @@ public CreateOAuth2(String authToken, String nickname, OAuth2Provider provider) @Getter public static class Update extends SelfValidating { + @NotBlank(message = "닉네임은 필수입니다.") private final String nickname;