Files
sb-backend/src/main/java/com/sb/web/auth/controller/AuthController.java
T
ByungCheolandClaude Opus 4.8 09efd65fa9 feat: 구글 로그인/가입 — ID 토큰 검증 후 세션 발급
- AuthService.googleLogin: 구글 tokeninfo 로 ID 토큰 검증(aud==클라이언트 ID)
  후 provider=GOOGLE 회원 조회/자동가입 → 세션 발급(login 과 동일 경로)
- GoogleLoginRequest DTO, /api/auth/google·/google-client-id 엔드포인트
- application.yml: app.google-client-id=${GOOGLE_CLIENT_ID:}

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 18:02:19 +09:00

108 lines
4.3 KiB
Java

package com.sb.web.auth.controller;
import com.sb.web.auth.dto.LoginRequest;
import com.sb.web.auth.dto.LoginResponse;
import com.sb.web.auth.dto.MemberResponse;
import com.sb.web.auth.dto.PasswordChangeRequest;
import com.sb.web.auth.dto.SessionUser;
import com.sb.web.auth.dto.SignupRequest;
import com.sb.web.auth.service.AuthService;
import com.sb.web.auth.web.AuthInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
* 인증 API.
* POST /api/auth/signup 회원가입 (비보호)
* POST /api/auth/login 로그인 (비보호)
* POST /api/auth/logout 로그아웃 (보호)
* GET /api/auth/me 내 정보 (보호)
*/
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
private final com.sb.web.admin.service.AppSettingService appSettingService;
/** 회원가입 허용 여부 (비보호) — 프론트가 가입 진입 전에 차단 표시용 */
@GetMapping("/signup-enabled")
public java.util.Map<String, Boolean> signupEnabled() {
return java.util.Map.of("enabled", appSettingService.isSignupEnabled());
}
@PostMapping("/signup")
public ResponseEntity<MemberResponse> signup(@Valid @RequestBody SignupRequest req,
HttpServletRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(authService.signup(req, clientIp(request)));
}
/** 클라이언트 IP (nginx 프록시 뒤 → X-Forwarded-For 우선) */
private String clientIp(HttpServletRequest request) {
String xff = request.getHeader("X-Forwarded-For");
if (xff != null && !xff.isBlank()) {
return xff.split(",")[0].trim();
}
return request.getRemoteAddr();
}
@PostMapping("/login")
public LoginResponse login(@Valid @RequestBody LoginRequest req, HttpServletRequest request) {
return authService.login(req, clientIp(request));
}
/** 구글 OAuth 클라이언트 ID (비보호) — 프론트가 구글 버튼 노출/초기화에 사용. 미설정 시 빈 문자열 */
@GetMapping("/google-client-id")
public java.util.Map<String, String> googleClientId() {
return java.util.Map.of("clientId", authService.googleClientId());
}
/** 구글 로그인/가입 (비보호) — ID 토큰 검증 후 세션 발급 */
@PostMapping("/google")
public LoginResponse googleLogin(@Valid @RequestBody com.sb.web.auth.dto.GoogleLoginRequest req) {
return authService.googleLogin(req.getIdToken(), req.isRememberMe());
}
@PostMapping("/logout")
public ResponseEntity<Void> logout(HttpServletRequest request) {
authService.logout(AuthInterceptor.resolveToken(request));
return ResponseEntity.noContent().build();
}
@GetMapping("/me")
public SessionUser me(@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
return current;
}
@PutMapping("/password")
public ResponseEntity<Void> changePassword(
@Valid @RequestBody PasswordChangeRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
authService.changePassword(current.getId(), req);
return ResponseEntity.noContent().build();
}
/** 가입정보 변경 진입 전 비밀번호 재인증 */
@PostMapping("/verify-password")
public ResponseEntity<Void> verifyPassword(
@Valid @RequestBody com.sb.web.auth.dto.PasswordVerifyRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
authService.verifyPassword(current.getId(), req.getPassword());
return ResponseEntity.noContent().build();
}
/** 가입정보(이름/이메일) 변경 */
@PutMapping("/profile")
public MemberResponse updateProfile(
@Valid @RequestBody com.sb.web.auth.dto.ProfileUpdateRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current,
HttpServletRequest request) {
return authService.updateProfile(current.getId(), AuthInterceptor.resolveToken(request), req);
}
}