From 91ff3956cd1727359d49fa437bc59e2dae885ddb Mon Sep 17 00:00:00 2001 From: Jaehyun Ahn <91878695+uwoobeat@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:39:41 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=EC=A0=84=EC=9A=A9=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#222)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 어드민 전용 로그인 API 구현 * feat: 비밀번호 미일치 상태코드 변경 --- .../gdsc/domain/auth/api/AuthController.java | 29 ++++++++++++ .../domain/auth/application/AuthService.java | 45 +++++++++++++++++++ .../domain/auth/dto/request/LoginRequest.java | 3 ++ .../auth/dto/response/LoginResponse.java | 8 ++++ .../domain/member/dao/MemberRepository.java | 2 + .../gdsc/global/config/WebSecurityConfig.java | 2 + .../gdsc/global/exception/ErrorCode.java | 2 + 7 files changed, 91 insertions(+) create mode 100644 src/main/java/com/gdschongik/gdsc/domain/auth/api/AuthController.java create mode 100644 src/main/java/com/gdschongik/gdsc/domain/auth/application/AuthService.java create mode 100644 src/main/java/com/gdschongik/gdsc/domain/auth/dto/request/LoginRequest.java create mode 100644 src/main/java/com/gdschongik/gdsc/domain/auth/dto/response/LoginResponse.java diff --git a/src/main/java/com/gdschongik/gdsc/domain/auth/api/AuthController.java b/src/main/java/com/gdschongik/gdsc/domain/auth/api/AuthController.java new file mode 100644 index 000000000..f0b09e4a1 --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/auth/api/AuthController.java @@ -0,0 +1,29 @@ +package com.gdschongik.gdsc.domain.auth.api; + +import com.gdschongik.gdsc.domain.auth.application.AuthService; +import com.gdschongik.gdsc.domain.auth.dto.request.LoginRequest; +import com.gdschongik.gdsc.domain.auth.dto.response.LoginResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +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; + +@Tag(name = "Auth", description = "어드민 인증 API입니다.") +@RestController +@RequestMapping("/auth") +@RequiredArgsConstructor +public class AuthController { + + private final AuthService authService; + + @Operation(summary = "로그인", description = "로그인을 수행합니다. 어드민만 가능합니다.") + @PostMapping("/login") + public ResponseEntity login(@RequestBody LoginRequest request) { + LoginResponse response = authService.loginAdmin(request); + return ResponseEntity.ok().body(response); + } +} diff --git a/src/main/java/com/gdschongik/gdsc/domain/auth/application/AuthService.java b/src/main/java/com/gdschongik/gdsc/domain/auth/application/AuthService.java new file mode 100644 index 000000000..956bdff4e --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/auth/application/AuthService.java @@ -0,0 +1,45 @@ +package com.gdschongik.gdsc.domain.auth.application; + +import com.gdschongik.gdsc.domain.auth.dto.request.LoginRequest; +import com.gdschongik.gdsc.domain.auth.dto.response.LoginResponse; +import com.gdschongik.gdsc.domain.member.dao.MemberRepository; +import com.gdschongik.gdsc.domain.member.domain.Member; +import com.gdschongik.gdsc.domain.member.domain.MemberRole; +import com.gdschongik.gdsc.global.exception.CustomException; +import com.gdschongik.gdsc.global.exception.ErrorCode; +import com.gdschongik.gdsc.global.property.SwaggerProperty; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AuthService { + + private final SwaggerProperty swaggerProperty; + private final JwtService jwtService; + private final MemberRepository memberRepository; + + public LoginResponse loginAdmin(LoginRequest request) { + Member member = memberRepository + .findByEmail(request.email()) + .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); + + if (member.getRole() != MemberRole.ADMIN) { + log.error("Invalid role"); + throw new CustomException(ErrorCode.INVALID_ROLE); + } + + if (!request.password().equals(swaggerProperty.getPassword())) { + log.error("Invalid password"); + throw new CustomException(ErrorCode.INVALID_PASSWORD); + } + + String accessToken = + jwtService.createAccessToken(member.getId(), member.getRole()).tokenValue(); + String refreshToken = jwtService.createRefreshToken(member.getId()).tokenValue(); + + return LoginResponse.from(accessToken, refreshToken); + } +} diff --git a/src/main/java/com/gdschongik/gdsc/domain/auth/dto/request/LoginRequest.java b/src/main/java/com/gdschongik/gdsc/domain/auth/dto/request/LoginRequest.java new file mode 100644 index 000000000..21b4317ed --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/auth/dto/request/LoginRequest.java @@ -0,0 +1,3 @@ +package com.gdschongik.gdsc.domain.auth.dto.request; + +public record LoginRequest(String email, String password) {} diff --git a/src/main/java/com/gdschongik/gdsc/domain/auth/dto/response/LoginResponse.java b/src/main/java/com/gdschongik/gdsc/domain/auth/dto/response/LoginResponse.java new file mode 100644 index 000000000..b542817fc --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/auth/dto/response/LoginResponse.java @@ -0,0 +1,8 @@ +package com.gdschongik.gdsc.domain.auth.dto.response; + +public record LoginResponse(String accessToken, String refreshToken) { + + public static LoginResponse from(String accessToken, String refreshToken) { + return new LoginResponse(accessToken, refreshToken); + } +} diff --git a/src/main/java/com/gdschongik/gdsc/domain/member/dao/MemberRepository.java b/src/main/java/com/gdschongik/gdsc/domain/member/dao/MemberRepository.java index bf8c40dd1..746f7513e 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/member/dao/MemberRepository.java +++ b/src/main/java/com/gdschongik/gdsc/domain/member/dao/MemberRepository.java @@ -12,4 +12,6 @@ public interface MemberRepository extends JpaRepository, MemberCus Optional findByDiscordUsername(String discordUsername); Optional findByUnivEmail(String univEmail); + + Optional findByEmail(String email); } diff --git a/src/main/java/com/gdschongik/gdsc/global/config/WebSecurityConfig.java b/src/main/java/com/gdschongik/gdsc/global/config/WebSecurityConfig.java index b22ac5abb..7814328fa 100644 --- a/src/main/java/com/gdschongik/gdsc/global/config/WebSecurityConfig.java +++ b/src/main/java/com/gdschongik/gdsc/global/config/WebSecurityConfig.java @@ -93,6 +93,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(authorize -> authorize .requestMatchers("/oauth2/**") .permitAll() + .requestMatchers("/auth/**") + .permitAll() .requestMatchers("/gdsc-actuator/**") .permitAll() .requestMatchers("/onboarding/verify-email") diff --git a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java index d096fcfb3..ee7e8ce32 100644 --- a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java +++ b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java @@ -19,6 +19,8 @@ public enum ErrorCode { AUTH_NOT_PARSABLE(HttpStatus.INTERNAL_SERVER_ERROR, "시큐리티 인증 정보 파싱에 실패했습니다."), BASE_URI_COOKIE_NOT_FOUND(HttpStatus.NOT_FOUND, "Base URI 쿠키가 존재하지 않습니다."), NOT_ALLOWED_BASE_URI(HttpStatus.FORBIDDEN, "허용되지 않은 Base URI입니다."), + INVALID_PASSWORD(HttpStatus.UNAUTHORIZED, "비밀번호가 일치하지 않습니다."), + INVALID_ROLE(HttpStatus.FORBIDDEN, "권한이 없습니다."), // Parameter INVALID_QUERY_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 쿼리 파라미터입니다."), From 14852b4d538309746dd7b0a7566200a6148a48c1 Mon Sep 17 00:00:00 2001 From: Jaehyun Ahn <91878695+uwoobeat@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:45:13 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=EC=BF=A0=ED=82=A4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=A0=84=EA=B9=8C=EC=A7=80=20=EB=A6=AC=EB=8B=A4?= =?UTF-8?q?=EC=9D=B4=EB=A0=89=ED=8A=B8=20URL=EC=9D=84=20=EC=98=A8=EB=B3=B4?= =?UTF-8?q?=EB=94=A9=EC=9C=BC=EB=A1=9C=20=EA=B3=A0=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20(#224)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: 리다이렉트 URL 고정 --- .../gdschongik/gdsc/global/security/CustomSuccessHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gdschongik/gdsc/global/security/CustomSuccessHandler.java b/src/main/java/com/gdschongik/gdsc/global/security/CustomSuccessHandler.java index e60e28240..f1b0c069a 100644 --- a/src/main/java/com/gdschongik/gdsc/global/security/CustomSuccessHandler.java +++ b/src/main/java/com/gdschongik/gdsc/global/security/CustomSuccessHandler.java @@ -34,7 +34,9 @@ public CustomSuccessHandler(JwtService jwtService, CookieUtil cookieUtil) { public void onAuthenticationSuccess( HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { - String baseUri = determineTargetUrl(request, response); + // TODO: 사파리 쿠키 이슈 해결 후 재활성화 + // String baseUri = determineTargetUrl(request, response); + String baseUri = PROD_CLIENT_ONBOARDING_URL; CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal();