Skip to content

Commit e07eb79

Browse files
committed
DASOMBE-17 로그인 관리자와 일반으로 분리
1 parent adab570 commit e07eb79

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;
@@ -49,6 +50,68 @@ public ResponseEntity<Void> signUp(@Valid @RequestBody final SignupRequestDto re
4950
return ResponseEntity.ok().build();
5051
}
5152

53+
// 일반 사용자 로그인
54+
@Operation(summary = "일반 사용자 로그인")
55+
@ApiResponses(value = {
56+
@ApiResponse(responseCode = "200", description = "로그인 성공 (Header로 토큰 반환)"),
57+
@ApiResponse(responseCode = "400", description = "실패 케이스",
58+
content = @Content(
59+
mediaType = "application/json",
60+
schema = @Schema(implementation = ErrorResponse.class),
61+
examples = {
62+
@ExampleObject(
63+
name = "회원 없음",
64+
value = "{ \"code\": \"C003\", \"message\": \"해당 회원을 찾을 수 없습니다.\" }"
65+
),
66+
@ExampleObject(
67+
name = "로그인 실패",
68+
value = "{ \"code\": \"C005\", \"message\": \"로그인에 실패하였습니다.\" }"
69+
),
70+
@ExampleObject(
71+
name = "권한 없음",
72+
value = "{ \"code\": \"C001\", \"message\": \"인증되지 않은 사용자입니다.\" }")}))})
73+
@PostMapping("/auth/user-login")
74+
public ResponseEntity<Void> userLogin(@Valid @RequestBody final LoginRequestDto request) {
75+
final TokenBox tokenBox = memberService.userLogin(request);
76+
final HttpHeaders headers = new HttpHeaders();
77+
headers.add("Access-Token", tokenBox.getAccessToken());
78+
headers.add("Refresh-Token", tokenBox.getRefreshToken());
79+
headers.add("Authority", tokenBox.getAuthority());
80+
81+
return ResponseEntity.ok().headers(headers).build();
82+
}
83+
84+
// 관리자 로그인
85+
@Operation(summary = "관리자 로그인")
86+
@ApiResponses(value = {
87+
@ApiResponse(responseCode = "200", description = "로그인 성공 (Header로 토큰 반환)"),
88+
@ApiResponse(responseCode = "400", description = "실패 케이스",
89+
content = @Content(
90+
mediaType = "application/json",
91+
schema = @Schema(implementation = ErrorResponse.class),
92+
examples = {
93+
@ExampleObject(
94+
name = "회원 없음",
95+
value = "{ \"code\": \"C003\", \"message\": \"해당 회원을 찾을 수 없습니다.\" }"
96+
),
97+
@ExampleObject(
98+
name = "로그인 실패",
99+
value = "{ \"code\": \"C005\", \"message\": \"로그인에 실패하였습니다.\" }"
100+
),
101+
@ExampleObject(
102+
name = "권한 없음",
103+
value = "{ \"code\": \"C001\", \"message\": \"인증되지 않은 사용자입니다.\" }")}))})
104+
@PostMapping("/auth/admin-login")
105+
public ResponseEntity<Void> adminLogin(@Valid @RequestBody final LoginRequestDto request) {
106+
final TokenBox tokenBox = memberService.adminLogin(request);
107+
final HttpHeaders headers = new HttpHeaders();
108+
headers.add("Access-Token", tokenBox.getAccessToken());
109+
headers.add("Refresh-Token", tokenBox.getRefreshToken());
110+
headers.add("Authority", tokenBox.getAuthority());
111+
112+
return ResponseEntity.ok().headers(headers).build();
113+
}
114+
52115
@Operation(summary = "토큰 갱신")
53116
@ApiResponses(value = {
54117
@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)