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, "잘못된 쿼리 파라미터입니다."), 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();