Skip to content

Commit

Permalink
Merge pull request #94 from Central-MakeUs/93-자체-로그인을-할-수-있다
Browse files Browse the repository at this point in the history
Feat(#93): 자체 로그인 추가
  • Loading branch information
tmddus2 authored Aug 24, 2024
2 parents 145d4c8 + 3ac9028 commit 756eb4f
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/dev-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ jobs:
working-directory: ${{ env.working-directory }}
run: |
chmod +x gradlew
./gradlew clean build
./gradlew clean build -Dspring.profiles.active=dev
2 changes: 1 addition & 1 deletion .github/workflows/prod-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ jobs:
working-directory: ${{ env.working-directory }}
run: |
chmod +x gradlew
./gradlew clean build
./gradlew clean build -Dspring.profiles.active=prod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@AllArgsConstructor
public enum Provider {
KAKAO("KAKAO"),
APPLE("APPLE");
APPLE("APPLE"),
PURITHM("PURITHM");
private final String provider;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class User {

private String email;

private String password;

@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
@Column(updatable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.example.purithm.domain.user.repository;

import java.util.Optional;

import com.example.purithm.domain.user.entity.Provider;
import com.example.purithm.domain.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -17,4 +19,8 @@ public interface UserRepository extends JpaRepository<User, Long> {

@Query("SELECT COUNT(ufl) FROM UserFilterLog ufl WHERE ufl.userId = :userId")
int countLogsByUserId(@Param("userId") Long userId);

boolean existsByProviderId(String id);

Optional<User> findByProviderId(String id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.example.purithm.domain.user.dto.request.UserInfoRequestDto;
import com.example.purithm.domain.user.dto.response.AccountInfoDto;
import com.example.purithm.domain.user.dto.response.UserInfoDto;
import com.example.purithm.global.auth.dto.response.SocialUserInfoDto;
import com.example.purithm.domain.user.entity.Provider;
import com.example.purithm.global.auth.dto.request.LoginRequestDto;
import com.example.purithm.global.auth.dto.response.SignUpUserInfoDto;
import com.example.purithm.domain.user.entity.User;
import com.example.purithm.domain.user.repository.UserRepository;
import com.example.purithm.global.exception.CustomException;
Expand All @@ -13,15 +15,22 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;

public Long signUp(SignUpUserInfoDto socialUserInfoDto) {
if (socialUserInfoDto.getProvider().equals(Provider.PURITHM)
&& userRepository.existsByProviderId(socialUserInfoDto.getProviderId())) {
throw CustomException.of(Error.NICKNAME_ALREADY_USED_ERROR);
}

public Long signUp(SocialUserInfoDto socialUserInfoDto) {
User existUser = userRepository
.findByProviderAndProviderId(socialUserInfoDto.getProvider(), socialUserInfoDto.getProviderId());

Expand All @@ -37,6 +46,7 @@ public Long signUp(SocialUserInfoDto socialUserInfoDto) {
.email(socialUserInfoDto.getEmail())
.terms(false)
.membership(Membership.BASIC)
.password(passwordEncoder.encode(socialUserInfoDto.getPassword()))
.build();

User savedUser = userRepository.save(user);
Expand Down Expand Up @@ -92,4 +102,14 @@ public void updateProfile(UserInfoRequestDto userInfo, Long userId) {
user.updateProfile(userInfo);
userRepository.save(user);
}

public Long getUserId(LoginRequestDto loginRequestDto) {
User user = userRepository.findByProviderId(loginRequestDto.id())
.orElseThrow(() -> CustomException.of(Error.NOT_FOUND_ERROR));

if (!passwordEncoder.matches(loginRequestDto.password(), user.getPassword())) {
throw CustomException.of(Error.INVALID_ID_PASSWORD);
}
return user.getId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import com.example.purithm.global.auth.argumentresolver.LoginUserArgumentResolver;
import java.util.List;
import lombok.RequiredArgsConstructor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

Expand All @@ -15,4 +19,9 @@ public class LoginConfig implements WebMvcConfigurer {
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginUserArgumentResolver);
}

@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.example.purithm.global.auth.controller;

import com.example.purithm.domain.user.entity.Provider;
import com.example.purithm.global.auth.dto.request.LoginRequestDto;
import com.example.purithm.global.auth.dto.request.SignUpRequestDto;
import com.example.purithm.global.auth.dto.response.KakaoUserInfoDto;
import com.example.purithm.global.auth.dto.response.LoginDto;
import com.example.purithm.global.auth.dto.response.SocialUserInfoDto;
import com.example.purithm.global.auth.dto.response.SignUpUserInfoDto;
import com.example.purithm.global.auth.jwt.JWTUtil;
import com.example.purithm.global.config.WebClientConfig;
import com.example.purithm.domain.user.service.UserService;
Expand All @@ -17,6 +19,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -33,6 +36,32 @@ public class AuthController implements AuthControllerDocs {
private final UserService userService;
private final JWTUtil jwtUtil;

@PostMapping("/login")
public SuccessResponse<LoginDto> login(LoginRequestDto loginRequestDto) {
Long id = userService.getUserId(loginRequestDto);
String jwtToken = jwtUtil.createJwt(id, 60 * 60 * 60 * 1000L);

LoginDto loginDto = LoginDto.builder()
.accessToken(jwtToken).build();
return SuccessResponse.of(loginDto);
}

@PostMapping("/signup")
public SuccessResponse signup(SignUpRequestDto signUpRequestDto) {
SignUpUserInfoDto userInfoDto = SignUpUserInfoDto.builder()
.profile(signUpRequestDto.profile())
.username(signUpRequestDto.username())
.provider(Provider.PURITHM)
.providerId(signUpRequestDto.id())
.email(signUpRequestDto.email())
.password(signUpRequestDto.password())
.build();

Long id = userService.signUp(userInfoDto);

return SuccessResponse.of();
}

@GetMapping("/kakao")
public Mono<SuccessResponse<LoginDto>> kakaoLogin(String token) {
return webClientConfig.webClient()
Expand All @@ -45,13 +74,14 @@ public Mono<SuccessResponse<LoginDto>> kakaoLogin(String token) {
.bodyToMono(KakaoUserInfoDto.class)
.flatMap(res -> {

SocialUserInfoDto userInfoDto = SocialUserInfoDto.builder()
.profile(res.getProperties().getProfile_image())
.username(res.getProperties().getNickname())
.provider(Provider.KAKAO)
.providerId(String.valueOf(res.getId()))
.email(res.getKakao_account().getEmail())
.build();
SignUpUserInfoDto userInfoDto = SignUpUserInfoDto.builder()
.profile(res.getProperties().getProfile_image())
.username(res.getProperties().getNickname())
.provider(Provider.KAKAO)
.providerId(String.valueOf(res.getId()))
.email(res.getKakao_account().getEmail())
.password(null)
.build();
Long id = userService.signUp(userInfoDto);
String jwtToken = jwtUtil.createJwt(id, 60 * 60 * 60 * 1000L);

Expand All @@ -72,12 +102,13 @@ public SuccessResponse<LoginDto> appleLogin(String token, String username)
try {
token = token.substring(7);
Claims claims = jwtUtil.getAppleTokenClaims(token);
SocialUserInfoDto userInfoDto = SocialUserInfoDto.builder()
SignUpUserInfoDto userInfoDto = SignUpUserInfoDto.builder()
.profile(null)
.username(username)
.provider(Provider.APPLE)
.providerId((String) claims.get("sub"))
.email((String) claims.get("email"))
.password(null)
.build();

Long id = userService.signUp(userInfoDto);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.example.purithm.global.auth.controller;

import com.example.purithm.global.auth.dto.request.LoginRequestDto;
import com.example.purithm.global.auth.dto.request.SignUpRequestDto;
import com.example.purithm.global.auth.dto.response.LoginDto;
import com.example.purithm.global.response.SuccessResponse;
import com.nimbusds.jose.JOSEException;
Expand All @@ -9,27 +11,37 @@
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.IOException;
import java.text.ParseException;

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;

import reactor.core.publisher.Mono;

public interface AuthControllerDocs {
@Operation(
@Operation(
summary = "자체 회원 가입"
)
public SuccessResponse signup(@RequestBody SignUpRequestDto signUpRequestDto);

@Operation(
summary = "자체 로그인"
)
public SuccessResponse<LoginDto> login(@RequestBody LoginRequestDto loginRequestDto);

@Operation(
summary = "Kakao Login",
parameters = {
@Parameter(name = "Authorization", description = "kakao access token을 보냅니다. Bearer token 형식입니다.", required = true, in = ParameterIn.HEADER)
}
)
public Mono<SuccessResponse<LoginDto>> kakaoLogin(@RequestHeader("Authorization") String token);
})
public Mono<SuccessResponse<LoginDto>> kakaoLogin(@RequestHeader("Authorization") String token);

@Operation(
@Operation(
summary = "Apple Login",
parameters = {
@Parameter(name = "Authorization", description = "Apple access token을 보냅니다. Bearer token 형식입니다.", required = true, in = ParameterIn.HEADER, schema = @Schema(type = "string"))
}
)
public SuccessResponse<LoginDto> appleLogin(
})
public SuccessResponse<LoginDto> appleLogin(
@RequestHeader("Authorization") String token, @RequestParam(value = "username", required = false) String username)
throws IOException, ParseException, JOSEException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.purithm.global.auth.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;

public record LoginRequestDto(
@Schema(description = "id")
String id,
@Schema(description = "password")
String password
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.purithm.global.auth.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;

public record SignUpRequestDto(
@Schema(description = "회원 id")
String id,
@Schema(description = "닉네임")
String username,
@Schema(description = "비밀 번호")
String password,
@Schema(description = "프로필 사진")
String profile,
@Schema(description = "email")
String email
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

@Getter
@Builder
public class SocialUserInfoDto {
public class SignUpUserInfoDto {
private String username;
private Provider provider;
private String providerId;
private String profile;
private String email;
private String password;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public enum Error {
/* 401 */
INVALID_TOKEN_ERROR(HttpStatus.UNAUTHORIZED, 40100, "유효하지 않은 토큰입니다."),
EXPIRED_TOKEN_ERROR(HttpStatus.UNAUTHORIZED, 40101, "만료된 토큰입니다."),
INVALID_ID_PASSWORD(HttpStatus.UNAUTHORIZED, 40102, "아이디/비밀번호가 적절하지 않습니다."),

/* 403 */
NOT_AGREED_TERM(HttpStatus.FORBIDDEN, 40300, "이용약관 동의가 필요합니다"),
Expand Down

0 comments on commit 756eb4f

Please sign in to comment.