문제점: JwtProvider에서 extractToken 메서드로 Bearer 접두사를 제거하도록 처리했음에도 불구하고 포스트맨에서 테스트할 때 토큰 값이 제대로 인식되지 않는 문제가 발생.
public String extractToken(String header) {
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7).trim(); // Bearer 접두사 제거
}
throw new BadValueException(ExceptionType.MISSING_BEARER_TOKEN);
}
원인: 컨트롤러나 서비스 등에서 token이라는 이름의 매개변수를 사용했고, 다른 계층에서도 동일한 token 이름을 사용하였는데, token이라는 이름이 너무 일반적이어서 코드의 의도를 명확히 드러내지 못했고, 이는 token 이라는 값이 HTTP 요청 헤더에서 가져온 값인지 아니면 메서드 내부에서 생성한 값인지 불명확해지는 문제로 이어졌다.
해결: 매개변수의 이름을 headerValue로 변경하여 해당 값이 HTTP 헤드에서 가져온 것임을 명확히 드러냈고 이로써 문제를 해결했다. (HTTP 헤더 값은 headerValue라는 이름으로, 토큰 자체는 token이라는 이름으로!)
// 테스트용
// INFO_UPDATE 토큰 발급
@PostMapping("/{id}/info-update-token")
public ResponseEntity<CommonResponseBody<JwtAuthResponse>> issueInfoUpdateToken(
@PathVariable Long id,
@RequestHeader(HttpHeaders.AUTHORIZATION) String headerValue) {
JwtAuthResponse authResponse = userService.issuePurposeToken(id, headerValue, "INFO_UPDATE");
return ResponseEntity.ok(new CommonResponseBody<>("INFO_UPDATE 토큰이 발급되었습니다.", authResponse));
}
// PASSWORD_CONFIRMATION 토큰 발급
@PostMapping("/{id}/password-confirmation-token")
public ResponseEntity<CommonResponseBody<JwtAuthResponse>> issuePasswordConfirmationToken(
@PathVariable Long id,
@Valid @RequestBody CheckRequestDto checkRequestDto) {
JwtAuthResponse authResponse = userService.checkPassword(id, checkRequestDto.getPassword());
return ResponseEntity.ok(new CommonResponseBody<>("PASSWORD_CONFIRMATION 토큰이 발급되었습니다.", authResponse));
}
// 회원정보 수정
@PatchMapping("/{id}")
public ResponseEntity<String> updateUserInfo(
@PathVariable Long id,
@RequestHeader(HttpHeaders.AUTHORIZATION) String headerValue,
@Valid @RequestBody UpdateUserRequestDto updateUserRequestDto) {
userService.validatePurposeToken(headerValue, id, "INFO_UPDATE");
userService.updateUserInfo(id, "INFO_UPDATE", updateUserRequestDto);
return ResponseEntity.status(HttpStatus.OK).body("회원 정보가 수정되었습니다.");
}
// 회원탈퇴
@DeleteMapping("/{id}")
public ResponseEntity<String> deleteUser(
@PathVariable Long id,
@RequestHeader(HttpHeaders.AUTHORIZATION) String passwordConfirmationToken) {
userService.validatePurposeToken(passwordConfirmationToken, id, "PASSWORD_CONFIRMATION");
userService.deleteUser(id, "PASSWORD_CONFIRMATION");
return ResponseEntity.status(HttpStatus.NO_CONTENT).body("회원탈퇴가 완료되었습니다.");
}
// 테스트용
// 목적별 토큰 발급
@Transactional
public JwtAuthResponse issuePurposeToken(Long userId, String headerValue, String purpose) {
// 토큰에서 사용자 ID 추출
String token = jwtProvider.extractToken(headerValue);
Long requesterId = jwtProvider.getUserId(token);
if (!userId.equals(requesterId)) {
throw new ForbiddenException(ExceptionType.FORBIDDEN_ACTION);
}
// 목적별 토큰 생성
String purposeToken = jwtProvider.generateToken(null, userId, purpose);
return new JwtAuthResponse(AuthenticationScheme.BEARER.getName(), purposeToken);
}
// 목적별 토큰 검증
public void validatePurposeToken(String headerValue, Long userId, String purpose) {
String token = jwtProvider.extractToken(headerValue);
if (!jwtProvider.validateToken(token, purpose)) {
throw new BadValueException(ExceptionType.INVALID_TOKEN_PURPOSE);
}
// 토큰에서 사용자 ID 추출
Long requesterId = jwtProvider.getUserId(token);
if (!userId.equals(requesterId)) {
throw new ForbiddenException(ExceptionType.FORBIDDEN_ACTION);
}
}
'Archive > 예전 TIL' 카테고리의 다른 글
| JPA 쿼리를 활용한 성능 개선 (0) | 2025.02.18 |
|---|---|
| ADMIN 권한 검증 시 에러 문제 (0) | 2025.02.11 |
| 트러블슈팅: plus_week (0) | 2024.12.20 |
| 트러블슈팅(: 아웃소싱 프로젝트) (0) | 2024.12.07 |
| 트러블 슈팅: MySQL 실행 오류(비밀번호 입력 시 창이 사라짐) (0) | 2024.11.15 |