Skip to content

Commit 19b3575

Browse files
committed
DASOMBE-17 로그인 관리자와 일반으로 분리
1 parent 4014fb4 commit 19b3575

File tree

5 files changed

+135
-3
lines changed

5 files changed

+135
-3
lines changed

src/main/java/dmu/dasom/api/domain/member/controller/MemberController.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dmu.dasom.api.domain.member.controller;
22

33
import dmu.dasom.api.domain.common.exception.ErrorResponse;
4+
import dmu.dasom.api.domain.member.dto.LoginRequestDto;
45
import dmu.dasom.api.domain.member.dto.SignupRequestDto;
56
import dmu.dasom.api.domain.member.service.MemberService;
67
import dmu.dasom.api.global.auth.dto.TokenBox;
@@ -47,6 +48,68 @@ public ResponseEntity<Void> signUp(@Valid @RequestBody final SignupRequestDto re
4748
return ResponseEntity.ok().build();
4849
}
4950

51+
// 일반 사용자 로그인
52+
@Operation(summary = "일반 사용자 로그인")
53+
@ApiResponses(value = {
54+
@ApiResponse(responseCode = "200", description = "로그인 성공 (Header로 토큰 반환)"),
55+
@ApiResponse(responseCode = "400", description = "실패 케이스",
56+
content = @Content(
57+
mediaType = "application/json",
58+
schema = @Schema(implementation = ErrorResponse.class),
59+
examples = {
60+
@ExampleObject(
61+
name = "회원 없음",
62+
value = "{ \"code\": \"C003\", \"message\": \"해당 회원을 찾을 수 없습니다.\" }"
63+
),
64+
@ExampleObject(
65+
name = "로그인 실패",
66+
value = "{ \"code\": \"C005\", \"message\": \"로그인에 실패하였습니다.\" }"
67+
),
68+
@ExampleObject(
69+
name = "권한 없음",
70+
value = "{ \"code\": \"C001\", \"message\": \"인증되지 않은 사용자입니다.\" }")}))})
71+
@PostMapping("/auth/user-login")
72+
public ResponseEntity<Void> userLogin(@Valid @RequestBody final LoginRequestDto request) {
73+
final TokenBox tokenBox = memberService.userLogin(request);
74+
final HttpHeaders headers = new HttpHeaders();
75+
headers.add("Access-Token", tokenBox.getAccessToken());
76+
headers.add("Refresh-Token", tokenBox.getRefreshToken());
77+
headers.add("Authority", tokenBox.getAuthority());
78+
79+
return ResponseEntity.ok().headers(headers).build();
80+
}
81+
82+
// 관리자 로그인
83+
@Operation(summary = "관리자 로그인")
84+
@ApiResponses(value = {
85+
@ApiResponse(responseCode = "200", description = "로그인 성공 (Header로 토큰 반환)"),
86+
@ApiResponse(responseCode = "400", description = "실패 케이스",
87+
content = @Content(
88+
mediaType = "application/json",
89+
schema = @Schema(implementation = ErrorResponse.class),
90+
examples = {
91+
@ExampleObject(
92+
name = "회원 없음",
93+
value = "{ \"code\": \"C003\", \"message\": \"해당 회원을 찾을 수 없습니다.\" }"
94+
),
95+
@ExampleObject(
96+
name = "로그인 실패",
97+
value = "{ \"code\": \"C005\", \"message\": \"로그인에 실패하였습니다.\" }"
98+
),
99+
@ExampleObject(
100+
name = "권한 없음",
101+
value = "{ \"code\": \"C001\", \"message\": \"인증되지 않은 사용자입니다.\" }")}))})
102+
@PostMapping("/auth/admin-login")
103+
public ResponseEntity<Void> adminLogin(@Valid @RequestBody final LoginRequestDto request) {
104+
final TokenBox tokenBox = memberService.adminLogin(request);
105+
final HttpHeaders headers = new HttpHeaders();
106+
headers.add("Access-Token", tokenBox.getAccessToken());
107+
headers.add("Refresh-Token", tokenBox.getRefreshToken());
108+
headers.add("Authority", tokenBox.getAuthority());
109+
110+
return ResponseEntity.ok().headers(headers).build();
111+
}
112+
50113
@Operation(summary = "토큰 갱신")
51114
@ApiResponses(value = {
52115
@ApiResponse(responseCode = "200", description = "토큰 갱신 성공 (Header로 토큰 반환)"),
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dmu.dasom.api.domain.member.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.Email;
5+
import jakarta.validation.constraints.NotNull;
6+
import jakarta.validation.constraints.Size;
7+
import lombok.Getter;
8+
9+
@Getter
10+
@Schema(name = "LoginRequestDto", description = "로그인 요청 DTO")
11+
public class LoginRequestDto {
12+
13+
@NotNull(message = "이메일은 필수 값입니다.")
14+
@Email(message = "유효한 이메일 주소를 입력해주세요.")
15+
@Size(max = 64)
16+
@Schema(description = "이메일 주소", example = "[email protected]")
17+
private String email;
18+
19+
@NotNull(message = "비밀번호는 필수 값입니다.")
20+
@Size(min = 8, max = 20, message = "비밀번호는 8자 이상 20자 이하로 입력해주세요.")
21+
@Schema(description = "비밀번호", example = "password123!")
22+
private String password;
23+
}

src/main/java/dmu/dasom/api/domain/member/service/MemberService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dmu.dasom.api.domain.member.service;
22

3+
import dmu.dasom.api.domain.member.dto.LoginRequestDto;
34
import dmu.dasom.api.domain.member.dto.SignupRequestDto;
45
import dmu.dasom.api.domain.member.entity.Member;
56
import dmu.dasom.api.global.auth.dto.TokenBox;
@@ -13,6 +14,12 @@ public interface MemberService {
1314

1415
void signUp(final SignupRequestDto request);
1516

17+
TokenBox login(final LoginRequestDto request);
18+
19+
TokenBox userLogin(final LoginRequestDto request);
20+
21+
TokenBox adminLogin(final LoginRequestDto request);
22+
1623
TokenBox tokenRotation(final UserDetailsImpl userDetails);
1724

1825
}

src/main/java/dmu/dasom/api/domain/member/service/MemberServiceImpl.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package dmu.dasom.api.domain.member.service;
22

3-
import dmu.dasom.api.domain.common.exception.CustomException;
4-
import dmu.dasom.api.domain.common.exception.ErrorCode;
3+
import dmu.dasom.api.domain.member.dto.LoginRequestDto;
54
import dmu.dasom.api.domain.member.dto.SignupRequestDto;
65
import dmu.dasom.api.domain.member.entity.Member;
6+
import dmu.dasom.api.domain.member.enums.Role;
77
import dmu.dasom.api.domain.member.repository.MemberRepository;
88
import dmu.dasom.api.global.auth.dto.TokenBox;
99
import dmu.dasom.api.global.auth.jwt.JwtUtil;
1010
import dmu.dasom.api.global.auth.userdetails.UserDetailsImpl;
11+
import dmu.dasom.api.domain.common.exception.CustomException;
12+
import dmu.dasom.api.domain.common.exception.ErrorCode;
1113
import lombok.RequiredArgsConstructor;
1214
import org.springframework.security.core.GrantedAuthority;
1315
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@@ -50,6 +52,43 @@ public void signUp(final SignupRequestDto request) {
5052
memberRepository.save(request.toEntity(encoder.encode(request.getPassword())));
5153
}
5254

55+
// 로그인 (기존 로직을 private 헬퍼 메소드로 변경)
56+
private TokenBox authenticateAndGenerateToken(final String email, final String password, final Role expectedRole) {
57+
// 1. 이메일로 사용자 조회
58+
final Member member = memberRepository.findByEmail(email)
59+
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));
60+
61+
// 2. 비밀번호 일치 여부 확인
62+
if (!encoder.matches(password, member.getPassword())) {
63+
throw new CustomException(ErrorCode.LOGIN_FAILED);
64+
}
65+
66+
// 3. 역할 확인
67+
if (expectedRole != null && member.getRole() != expectedRole) {
68+
throw new CustomException(ErrorCode.UNAUTHORIZED); // 또는 더 구체적인 에러 코드
69+
}
70+
71+
// 4. JWT 토큰 생성 및 반환
72+
return jwtUtil.generateTokenBox(member.getEmail(), member.getRole().getName());
73+
}
74+
75+
@Override
76+
public TokenBox login(final LoginRequestDto request) {
77+
// 이 메소드는 더 이상 사용되지 않거나, userLogin/adminLogin으로 대체됩니다.
78+
// 여기서는 임시로 일반 로그인으로 처리합니다.
79+
return authenticateAndGenerateToken(request.getEmail(), request.getPassword(), null);
80+
}
81+
82+
@Override
83+
public TokenBox userLogin(final LoginRequestDto request) {
84+
return authenticateAndGenerateToken(request.getEmail(), request.getPassword(), Role.ROLE_MEMBER);
85+
}
86+
87+
@Override
88+
public TokenBox adminLogin(final LoginRequestDto request) {
89+
return authenticateAndGenerateToken(request.getEmail(), request.getPassword(), Role.ROLE_ADMIN);
90+
}
91+
5392
// 토큰 갱신
5493
@Override
5594
public TokenBox tokenRotation(final UserDetailsImpl userDetails) {

src/main/java/dmu/dasom/api/global/auth/config/SecurityConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public SecurityFilterChain filterChain(final HttpSecurity http, final Authentica
7171
.requestMatchers("/api/auth/logout", "/api/auth/rotation").authenticated()
7272
.anyRequest().permitAll())
7373
.addFilterBefore(jwtFilter, CustomAuthenticationFilter.class)
74-
.addFilterAt(customAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
74+
7575
.addFilterAfter(new CustomLogoutFilter(jwtUtil), JwtFilter.class)
7676
.exceptionHandling(handler -> handler
7777
.accessDeniedHandler(accessDeniedHandler)

0 commit comments

Comments
 (0)