Skip to content

Commit 42115a2

Browse files
authored
Merge pull request #231 from CSE-Shaco/develop
Feat: 권한 검증 엔드포인트 추가 및 UserRole 기반 비교 로직 개선
2 parents bedf24b + f4e4c54 commit 42115a2

File tree

2 files changed

+69
-55
lines changed

2 files changed

+69
-55
lines changed

src/main/java/inha/gdgoc/domain/auth/controller/AuthController.java

Lines changed: 46 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
11
package inha.gdgoc.domain.auth.controller;
22

3-
import static inha.gdgoc.domain.auth.controller.message.AuthMessage.ACCESS_TOKEN_REFRESH_SUCCESS;
4-
import static inha.gdgoc.domain.auth.controller.message.AuthMessage.CODE_CREATION_SUCCESS;
5-
import static inha.gdgoc.domain.auth.controller.message.AuthMessage.LOGIN_WITH_PASSWORD_SUCCESS;
6-
import static inha.gdgoc.domain.auth.controller.message.AuthMessage.LOGOUT_SUCCESS;
7-
import static inha.gdgoc.domain.auth.controller.message.AuthMessage.OAUTH_LOGIN_SIGNUP_SUCCESS;
8-
import static inha.gdgoc.domain.auth.controller.message.AuthMessage.PASSWORD_CHANGE_SUCCESS;
9-
import static inha.gdgoc.domain.auth.controller.message.AuthMessage.PASSWORD_RESET_VERIFICATION_SUCCESS;
10-
import static inha.gdgoc.domain.auth.exception.AuthErrorCode.UNAUTHORIZED_USER;
11-
import static inha.gdgoc.domain.auth.exception.AuthErrorCode.USER_NOT_FOUND;
12-
133
import inha.gdgoc.domain.auth.dto.request.CodeVerificationRequest;
144
import inha.gdgoc.domain.auth.dto.request.PasswordResetRequest;
155
import inha.gdgoc.domain.auth.dto.request.SendingCodeRequest;
@@ -24,29 +14,31 @@
2414
import inha.gdgoc.domain.auth.service.MailService;
2515
import inha.gdgoc.domain.auth.service.RefreshTokenService;
2616
import inha.gdgoc.domain.user.entity.User;
17+
import inha.gdgoc.domain.user.enums.UserRole;
2718
import inha.gdgoc.domain.user.repository.UserRepository;
2819
import inha.gdgoc.global.config.jwt.TokenProvider;
2920
import inha.gdgoc.global.dto.response.ApiResponse;
21+
import inha.gdgoc.global.exception.GlobalErrorCode;
3022
import jakarta.servlet.http.HttpServletResponse;
31-
import java.security.InvalidKeyException;
32-
import java.security.NoSuchAlgorithmException;
33-
import java.util.Map;
34-
import java.util.Optional;
35-
3623
import jakarta.validation.Valid;
3724
import lombok.RequiredArgsConstructor;
3825
import lombok.extern.slf4j.Slf4j;
26+
import org.springframework.http.HttpStatus;
3927
import org.springframework.http.ResponseEntity;
4028
import org.springframework.security.access.prepost.PreAuthorize;
4129
import org.springframework.security.core.Authentication;
30+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
4231
import org.springframework.security.core.context.SecurityContextHolder;
43-
import org.springframework.web.bind.annotation.CookieValue;
44-
import org.springframework.web.bind.annotation.GetMapping;
45-
import org.springframework.web.bind.annotation.PostMapping;
46-
import org.springframework.web.bind.annotation.RequestBody;
47-
import org.springframework.web.bind.annotation.RequestMapping;
48-
import org.springframework.web.bind.annotation.RequestParam;
49-
import org.springframework.web.bind.annotation.RestController;
32+
import org.springframework.web.bind.annotation.*;
33+
34+
import java.security.InvalidKeyException;
35+
import java.security.NoSuchAlgorithmException;
36+
import java.util.Map;
37+
import java.util.Optional;
38+
39+
import static inha.gdgoc.domain.auth.controller.message.AuthMessage.*;
40+
import static inha.gdgoc.domain.auth.exception.AuthErrorCode.UNAUTHORIZED_USER;
41+
import static inha.gdgoc.domain.auth.exception.AuthErrorCode.USER_NOT_FOUND;
5042

5143
@Slf4j
5244
@RequestMapping("/api/v1/auth")
@@ -61,19 +53,14 @@ public class AuthController {
6153
private final AuthCodeService authCodeService;
6254

6355
@GetMapping("/oauth2/google/callback")
64-
public ResponseEntity<ApiResponse<Map<String, Object>, Void>> handleGoogleCallback(
65-
@RequestParam String code,
66-
HttpServletResponse response
67-
) {
56+
public ResponseEntity<ApiResponse<Map<String, Object>, Void>> handleGoogleCallback(@RequestParam String code, HttpServletResponse response) {
6857
Map<String, Object> data = authService.processOAuthLogin(code, response);
6958

7059
return ResponseEntity.ok(ApiResponse.ok(OAUTH_LOGIN_SIGNUP_SUCCESS, data));
7160
}
7261

7362
@PostMapping("/refresh")
74-
public ResponseEntity<?> refreshAccessToken(
75-
@CookieValue(value = "refresh_token", required = false) String refreshToken
76-
) {
63+
public ResponseEntity<?> refreshAccessToken(@CookieValue(value = "refresh_token", required = false) String refreshToken) {
7764
log.info("리프레시 토큰 요청 받음. 토큰 존재 여부: {}", refreshToken != null);
7865

7966
if (refreshToken == null) {
@@ -84,19 +71,15 @@ public ResponseEntity<?> refreshAccessToken(
8471
String newAccessToken = refreshTokenService.refreshAccessToken(refreshToken);
8572
AccessTokenResponse accessTokenResponse = new AccessTokenResponse(newAccessToken);
8673

87-
return ResponseEntity.ok(
88-
ApiResponse.ok(ACCESS_TOKEN_REFRESH_SUCCESS, accessTokenResponse, null));
74+
return ResponseEntity.ok(ApiResponse.ok(ACCESS_TOKEN_REFRESH_SUCCESS, accessTokenResponse, null));
8975
} catch (Exception e) {
9076
log.error("리프레시 토큰 처리 중 오류: {}", e.getMessage(), e);
9177
throw new AuthException(AuthErrorCode.INVALID_REFRESH_TOKEN);
9278
}
9379
}
9480

9581
@PostMapping("/login")
96-
public ResponseEntity<ApiResponse<LoginResponse, Void>> login(
97-
@Valid @RequestBody UserLoginRequest req,
98-
HttpServletResponse response
99-
) throws NoSuchAlgorithmException, InvalidKeyException {
82+
public ResponseEntity<ApiResponse<LoginResponse, Void>> login(@Valid @RequestBody UserLoginRequest req, HttpServletResponse response) throws NoSuchAlgorithmException, InvalidKeyException {
10083
String email = req.email().trim();
10184
LoginResponse loginResponse = authService.loginWithPassword(email, req.password(), response);
10285
return ResponseEntity.ok(ApiResponse.ok(LOGIN_WITH_PASSWORD_SUCCESS, loginResponse));
@@ -109,9 +92,7 @@ public ResponseEntity<ApiResponse<Void, Void>> logout() {
10992
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
11093

11194
// 1) 익명 방어
112-
if (authentication == null
113-
|| !authentication.isAuthenticated()
114-
|| "anonymousUser".equals(authentication.getName())) {
95+
if (authentication == null || !authentication.isAuthenticated() || "anonymousUser".equals(authentication.getName())) {
11596
throw new AuthException(UNAUTHORIZED_USER);
11697
}
11798

@@ -142,12 +123,9 @@ public ResponseEntity<ApiResponse<Void, Void>> logout() {
142123
}
143124

144125
@PostMapping("/password-reset/request")
145-
public ResponseEntity<ApiResponse<Void, Void>> responseResponseEntity(
146-
@RequestBody SendingCodeRequest sendingCodeRequest
147-
) {
126+
public ResponseEntity<ApiResponse<Void, Void>> responseResponseEntity(@RequestBody SendingCodeRequest sendingCodeRequest) {
148127
// TODO 서비스로 넘기기
149-
if (userRepository.existsByNameAndEmail(sendingCodeRequest.name(),
150-
sendingCodeRequest.email())) {
128+
if (userRepository.existsByNameAndEmail(sendingCodeRequest.name(), sendingCodeRequest.email())) {
151129
String code = mailService.sendAuthCode(sendingCodeRequest.email());
152130
authCodeService.saveAuthCode(sendingCodeRequest.email(), code);
153131

@@ -157,9 +135,7 @@ public ResponseEntity<ApiResponse<Void, Void>> responseResponseEntity(
157135
}
158136

159137
@PostMapping("/password-reset/verify")
160-
public ResponseEntity<ApiResponse<CodeVerificationResponse, Void>> verifyCode(
161-
@RequestBody CodeVerificationRequest request
162-
) {
138+
public ResponseEntity<ApiResponse<CodeVerificationResponse, Void>> verifyCode(@RequestBody CodeVerificationRequest request) {
163139
// TODO 서비스 단 DTO 추가
164140
boolean verified = authCodeService.verify(request.email(), request.code());
165141
CodeVerificationResponse response = new CodeVerificationResponse(verified);
@@ -168,9 +144,7 @@ public ResponseEntity<ApiResponse<CodeVerificationResponse, Void>> verifyCode(
168144
}
169145

170146
@PostMapping("/password-reset/confirm")
171-
public ResponseEntity<ApiResponse<Void, Void>> resetPassword(
172-
@RequestBody PasswordResetRequest passwordResetRequest
173-
) throws NoSuchAlgorithmException, InvalidKeyException {
147+
public ResponseEntity<ApiResponse<Void, Void>> resetPassword(@RequestBody PasswordResetRequest passwordResetRequest) throws NoSuchAlgorithmException, InvalidKeyException {
174148
// TODO 서비스 단으로
175149
Optional<User> user = userRepository.findByEmail(passwordResetRequest.email());
176150
if (user.isEmpty()) {
@@ -183,4 +157,27 @@ public ResponseEntity<ApiResponse<Void, Void>> resetPassword(
183157

184158
return ResponseEntity.ok(ApiResponse.ok(PASSWORD_CHANGE_SUCCESS));
185159
}
160+
161+
/**
162+
* 요구 권한(role) 이상이면 200, 아니면 403
163+
* 미인증이면 401
164+
165+
* 예) /api/v1/auth/LEAD, /api/v1/auth/ORGANIZER, /api/v1/auth/ADMIN
166+
*/
167+
@GetMapping("/{role}")
168+
public ResponseEntity<ApiResponse<Void, ?>> checkRole(@AuthenticationPrincipal TokenProvider.CustomUserDetails me, @PathVariable UserRole role) {
169+
if (me == null) {
170+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
171+
.body(ApiResponse.error(GlobalErrorCode.UNAUTHORIZED_USER.getStatus()
172+
.value(), GlobalErrorCode.UNAUTHORIZED_USER.getMessage(), null));
173+
}
174+
175+
if (UserRole.hasAtLeast(me.getRole(), role)) {
176+
return ResponseEntity.ok(ApiResponse.ok("ROLE_CHECK_PASSED", null));
177+
}
178+
179+
return ResponseEntity.status(HttpStatus.FORBIDDEN)
180+
.body(ApiResponse.error(GlobalErrorCode.FORBIDDEN_USER.getStatus()
181+
.value(), GlobalErrorCode.FORBIDDEN_USER.getMessage(), null));
182+
}
186183
}

src/main/java/inha/gdgoc/domain/user/enums/UserRole.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,33 @@
44

55
@Getter
66
public enum UserRole {
7-
GUEST("GUEST"),
8-
MEMBER("MEMBER"),
9-
CORE("CORE"),
10-
LEAD("LEAD"),
11-
ORGANIZER("ORGANIZER"),
12-
ADMIN("ADMIN");
7+
GUEST("GUEST"), MEMBER("MEMBER"), CORE("CORE"), LEAD("LEAD"), ORGANIZER("ORGANIZER"), ADMIN("ADMIN");
138

149
private final String role;
1510

1611
UserRole(String role) {
1712
this.role = role;
1813
}
14+
15+
/**
16+
* 나(me)가 required 이상 권한인지
17+
*/
18+
public static boolean hasAtLeast(UserRole me, UserRole required) {
19+
if (me == null || required == null) return false;
20+
return me.rank() >= required.rank();
21+
}
22+
23+
/**
24+
* 역할 서열(낮음→높음). enum 순서 바뀌어도 여기만 수정하면 됨
25+
*/
26+
public int rank() {
27+
return switch (this) {
28+
case GUEST -> 0;
29+
case MEMBER -> 1;
30+
case CORE -> 2;
31+
case LEAD -> 3;
32+
case ORGANIZER -> 4;
33+
case ADMIN -> 5;
34+
};
35+
}
1936
}

0 commit comments

Comments
 (0)