Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

46 changes: 0 additions & 46 deletions back/src/main/java/com/back/domain/session/entity/Session.java

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.back.domain.user.controller;

import com.back.domain.user.dto.LoginRequest;
import com.back.domain.user.dto.SignupRequest;
import com.back.domain.user.dto.UserResponse;
import com.back.domain.user.entity.User;
import com.back.domain.user.service.GuestService;
import com.back.domain.user.service.UserService;
import com.back.global.common.ApiResponse;
import com.back.global.security.CustomUserDetails;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.web.bind.annotation.*;

/**
* 사용자 인증/인가 관련 기능을 제공하는 REST 컨트롤러입니다.
* - 회원가입 (/signup): 새로운 사용자를 등록합니다.
* - 로그인 (/login): 이메일과 비밀번호를 이용해 인증 후 세션을 생성합니다.
* - 게스트 로그인 (/guest): 비회원 사용자용 임시 계정을 생성하고 로그인 처리합니다.
* - 현재 사용자 조회 (/me): 현재 인증된 사용자의 정보를 반환합니다.
*/
@RestController
@RequestMapping("/api/v1/users-auth")
@RequiredArgsConstructor
public class UserAuthController {

private final UserService userService;
private final AuthenticationManager authenticationManager;
private final GuestService guestService;

@PostMapping("/signup")
public ResponseEntity<ApiResponse<UserResponse>> signup(@Valid @RequestBody SignupRequest req){
User saved = userService.signup(req);
return ResponseEntity.status(HttpStatus.CREATED)
.body(ApiResponse.success(UserResponse.from(saved), "성공적으로 생성되었습니다."));
}

@PostMapping("/login")
public ResponseEntity<ApiResponse<UserResponse>> login(
@Valid @RequestBody LoginRequest req,
HttpServletRequest request,
HttpServletResponse response)
{
Authentication auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(req.email(), req.password()));
SecurityContextHolder.getContext().setAuthentication(auth);

request.getSession(true);
new HttpSessionSecurityContextRepository()
.saveContext(SecurityContextHolder.getContext(), request, response);

CustomUserDetails cud = (CustomUserDetails) auth.getPrincipal();
return ResponseEntity.ok(ApiResponse.success(UserResponse.from(cud.getUser()), "로그인 성공"));
}

@PostMapping("/guest")
public ResponseEntity<ApiResponse<UserResponse>> guestLogin(HttpServletRequest request, HttpServletResponse response){
User savedGuest = guestService.createAndSaveGuest();

CustomUserDetails cud = new CustomUserDetails(savedGuest);
Authentication auth = new UsernamePasswordAuthenticationToken(cud, null, cud.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);

request.getSession(true);
new HttpSessionSecurityContextRepository()
.saveContext(SecurityContextHolder.getContext(), request, response);

return ResponseEntity.ok(ApiResponse.success(UserResponse.from(savedGuest), "게스트 로그인 성공"));
}

@GetMapping("/me")
public ResponseEntity<ApiResponse<UserResponse>> me(@AuthenticationPrincipal CustomUserDetails cud) {
if (cud == null) {
return ResponseEntity.ok(ApiResponse.success(null, "anonymous"));
}
return ResponseEntity.ok(ApiResponse.success(UserResponse.from(cud.getUser()), "authenticated"));
}
}
15 changes: 15 additions & 0 deletions back/src/main/java/com/back/domain/user/dto/LoginRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.back.domain.user.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

/**
* 사용자 로그인 요청 시 필요한 정보를 담는 DTO 클래스.
* 로그인 ID와 비밀번호를 포함합니다.
*/
public record LoginRequest(
@Email @NotBlank(message = "로그인 아이디는 필수 입력 값입니다.")
String email,
@NotBlank(message = "비밀번호는 필수 입력 값입니다.")
String password
) {}
33 changes: 33 additions & 0 deletions back/src/main/java/com/back/domain/user/dto/SignupRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.back.domain.user.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.*;

import java.time.LocalDateTime;

/**
* 사용자 회원가입 요청 시 필요한 정보를 담는 DTO 클래스.
* 로그인 ID, 이메일, 비밀번호, 사용자 이름, 닉네임, 생년월일을 포함합니다.
*/
public record SignupRequest(

@NotBlank(message = "이메일은 필수 입력 값입니다.")
@Email(message = "이메일 형식에 맞지 않습니다.")
String email,

@NotBlank(message = "비밀번호는 필수 입력 값입니다.")
@Pattern(regexp="(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,20}",
message="비밀번호는 영문, 숫자, 특수기호 포함 8~20자여야 합니다.")
String password,

@NotBlank(message = "이름은 필수 입력 값입니다.")
String username,

@NotBlank(message = "닉네임은 필수 입력 값입니다.")
String nickname,

@NotNull(message = "생년월일은 필수 입력 값입니다.")
@Past(message = "생년월일은 과거 날짜여야 합니다.")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") // JSON 바인딩용(폼 바인딩이면 @DateTimeFormat)
LocalDateTime birthdayAt
) {}
27 changes: 27 additions & 0 deletions back/src/main/java/com/back/domain/user/dto/UserResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.back.domain.user.dto;

import com.back.domain.user.entity.Role;
import com.back.domain.user.entity.User;

import java.time.LocalDateTime;

/**
* 사용자 정보를 클라이언트에 반환하기 위한 응답 DTO입니다.
*/
public record UserResponse(
Long id,
String email,
String username,
Role role,
String nickname,
LocalDateTime birthdayAt,
String authProvider
) {
public static UserResponse from(User u) {
return new UserResponse(
u.getId(), u.getEmail(), u.getUsername(),
u.getRole(), u.getNickname(), u.getBirthdayAt(),
u.getAuthProvider() == null ? "" : u.getAuthProvider().name()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
* 일반 로그인, 소셜 로그인(Google, Kakao, Naver, GitHub, Apple), 게스트 로그인 등을 포함합니다.
*/
public enum AuthProvider {
LOCAL, GOOGLE, KAKAO, NAVER, GITHUB, APPLE, GUEST
LOCAL, GOOGLE, GITHUB, GUEST
}
Loading