Skip to content

Commit

Permalink
Code Refactoring: Token 관련 로직과 컨트롤러 분리, 별도 예외 클래스 생성
Browse files Browse the repository at this point in the history
  • Loading branch information
KiSeungMin committed Jan 28, 2025
1 parent 0695dde commit d5aa617
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
import com.aisip.OnO.backend.service.FolderService;
import com.aisip.OnO.backend.service.ProblemService;
import com.aisip.OnO.backend.service.UserService;
import io.sentry.Sentry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
Expand Down Expand Up @@ -66,33 +65,21 @@ public String getAllUsers(Model model, Authentication authentication) {

@PostMapping("/user/{userId}")
public String updateUserInfo(@PathVariable Long userId, @ModelAttribute UserRegisterDto userRegisterDto, Model model) {
UserResponseDto userResponseDto = userService.updateUser(userId, userRegisterDto);
model.addAttribute("user", userResponseDto);
List<ProblemResponseDto> problems = problemService.findUserProblems(userId);
model.addAttribute("problems", problems);

try {
UserResponseDto userResponseDto = userService.updateUser(userId, userRegisterDto);
if (userResponseDto != null) {
model.addAttribute("user", userResponseDto);
List<ProblemResponseDto> problems = problemService.findUserProblems(userId);
model.addAttribute("problems", problems);
return "user";
} else {
throw new Exception("can't find user!");
//return "users";
}
} catch (Exception e) {
log.warn(e.getMessage());
Sentry.captureException(e);
return "users";
}
return "user";
}

@ResponseStatus(HttpStatus.OK)
@DeleteMapping("/user/{userId}")
public ResponseEntity<?> deleteUserInfo(@PathVariable Long userId) {
public void deleteUserInfo(@PathVariable Long userId) {

problemService.deleteUserProblems(userId);
folderService.deleteAllUserFolder(userId);
userService.deleteUserById(userId);

return ResponseEntity.ok().body("delete complete");
}

@GetMapping("/problems")
Expand Down
114 changes: 22 additions & 92 deletions src/main/java/com/aisip/OnO/backend/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package com.aisip.OnO.backend.controller;

import com.aisip.OnO.backend.Dto.ErrorResponseDto;
import com.aisip.OnO.backend.Dto.Token.TokenRequestDto;
import com.aisip.OnO.backend.Dto.Token.TokenResponseDto;
import com.aisip.OnO.backend.Dto.User.UserRegisterDto;
import com.aisip.OnO.backend.Dto.User.UserResponseDto;
import com.aisip.OnO.backend.entity.User.UserType;
import com.aisip.OnO.backend.Auth.JwtTokenProvider;
import com.aisip.OnO.backend.service.JwtTokenService;
import com.aisip.OnO.backend.service.UserService;
import io.sentry.Sentry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

Expand All @@ -22,105 +19,38 @@
@RequestMapping("/api/auth")
public class AuthController {

private final JwtTokenProvider jwtTokenProvider;

private final JwtTokenService jwtTokenService;
private final UserService userService;


// 게스트 로그인
@ResponseStatus(HttpStatus.OK)
@PostMapping("/login/guest")
public ResponseEntity<?> guestLogin() {
try{
UserResponseDto user = userService.registerGuestUser();
return getUserTokenResponse(user);

} catch (Exception e) {
log.warn(e.getMessage());
Sentry.captureException(e);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponseDto(e.getMessage()));
}
public TokenResponseDto guestLogin() {
UserResponseDto user = userService.registerGuestUser();
return jwtTokenService.generateTokens(user);
}

// 소셜 로그인
@ResponseStatus(HttpStatus.OK)
@PostMapping("/login/social")
public ResponseEntity<?> socialLogin(@RequestBody UserRegisterDto userRegisterDto) {
log.info("Starting login with user info : " + userRegisterDto.toString());

try {
UserResponseDto user = userService.registerOrLoginUser(userRegisterDto, UserType.MEMBER);
return getUserTokenResponse(user);
} catch (Exception e) {
log.warn(e.getMessage());
Sentry.captureException(e);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponseDto(e.getMessage()));
}
public TokenResponseDto socialLogin(@RequestBody UserRegisterDto userRegisterDto) {
log.info("Starting login with user info: {}", userRegisterDto);
UserResponseDto user = userService.registerOrLoginUser(userRegisterDto, UserType.MEMBER);
return jwtTokenService.generateTokens(user);
}

// Access Token 검증
@ResponseStatus(HttpStatus.OK)
@GetMapping("/verifyAccessToken")
public ResponseEntity<?> verifyAccessToken(@RequestHeader("Authorization") String authorizationHeader) {
try {
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
String accessToken = authorizationHeader.substring(7);
if (jwtTokenProvider.validateToken(accessToken)) {
//log.info("success for verify access token");
return ResponseEntity.ok("Token is valid");
} else {
log.warn("token is invalid");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponseDto("Invalid or expired access token"));
}
} else {
log.warn("Authorization header missing or invalid");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorResponseDto("Authorization header missing or invalid"));
}
} catch (Exception e) {
log.warn(e.getMessage());
Sentry.captureException(e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorResponseDto("Token verification failed"));
}
public String verifyAccessToken(@RequestHeader("Authorization") String authorizationHeader) {
jwtTokenService.verifyAccessToken(authorizationHeader);
return "Token is valid";
}

// Refresh Token을 이용한 Access Token 재발급
@ResponseStatus(HttpStatus.OK)
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(@RequestBody TokenRequestDto tokenRequestDto) {
try {
String requestRefreshToken = tokenRequestDto.getRefreshToken();
log.info("start refresh token");
if (jwtTokenProvider.validateToken(requestRefreshToken)) {
Long userId = Long.parseLong(jwtTokenProvider.getSubjectFromToken(requestRefreshToken));
String newAccessToken = jwtTokenProvider.createAccessToken(userId);

log.info("Refresh Token Success for userId: " + userId);
return ResponseEntity.ok(new TokenResponseDto(newAccessToken, requestRefreshToken));
} else {
log.warn("Invalid or expired refresh token");

Long userId = Long.parseLong(jwtTokenProvider.getSubjectFromToken(requestRefreshToken));

String newAccessToken = jwtTokenProvider.createAccessToken(userId);
String newRefreshToken = jwtTokenProvider.createRefreshToken(userId);

return ResponseEntity.ok(new TokenResponseDto(newAccessToken, newRefreshToken));
}
} catch (Exception e) {
log.warn("Could not refresh access token");
Sentry.captureException(e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorResponseDto("Could not refresh access token"));
}
}


private ResponseEntity<?> getUserTokenResponse(UserResponseDto user){
try {
String accessToken = jwtTokenProvider.createAccessToken(user.getUserId());
String refreshToken = jwtTokenProvider.createRefreshToken(user.getUserId());

log.info(accessToken);
log.info(refreshToken);
log.info("id: " + user.getUserId() + ", name: " + user.getUserName() + " has login");

return ResponseEntity.ok(new TokenResponseDto(accessToken, refreshToken));
} catch (Exception e) {
log.warn(e.getMessage());
Sentry.captureException(e);

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorResponseDto("Could not Generate user token response"));
}
public TokenResponseDto refreshToken(@RequestBody TokenRequestDto tokenRequestDto) {
return jwtTokenService.refreshAccessToken(tokenRequestDto.getRefreshToken());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.aisip.OnO.backend.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.UNAUTHORIZED) // 401 Unauthorized
public class ExpiredTokenException extends TokenException {
public ExpiredTokenException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.aisip.OnO.backend.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.UNAUTHORIZED) // 401 Unauthorized
public class InvalidTokenException extends TokenException {
public InvalidTokenException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.aisip.OnO.backend.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.BAD_REQUEST) // 400 Bad Request
public class MissingTokenException extends TokenException {
public MissingTokenException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.aisip.OnO.backend.exception;


public class TokenException extends RuntimeException {
public TokenException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ public ResponseEntity<Map<String, Object>> handleForbiddenExceptions(RuntimeExce
return createErrorResponse(HttpStatus.FORBIDDEN, ex.getMessage());
}

// UNAUTHORIZED (401) 예외
@ExceptionHandler({
InvalidTokenException.class,
ExpiredTokenException.class
})
public ResponseEntity<Map<String, Object>> handleUnauthorizedTokenExceptions(TokenException ex) {
return createErrorResponse(HttpStatus.UNAUTHORIZED, ex.getMessage());
}

// Bad Request (400) 예외
@ExceptionHandler(MissingTokenException.class)
public ResponseEntity<Map<String, Object>> handleBadRequestTokenExceptions(MissingTokenException ex) {
return createErrorResponse(HttpStatus.BAD_REQUEST, ex.getMessage());
}

// 📌 기타 모든 예외 처리 (예상하지 못한 예외)
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleGeneralException(Exception ex) {
Expand Down
62 changes: 62 additions & 0 deletions src/main/java/com/aisip/OnO/backend/service/JwtTokenService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.aisip.OnO.backend.service;

import com.aisip.OnO.backend.Auth.JwtTokenProvider;
import com.aisip.OnO.backend.Dto.Token.TokenResponseDto;
import com.aisip.OnO.backend.Dto.User.UserResponseDto;
import com.aisip.OnO.backend.exception.ExpiredTokenException;
import com.aisip.OnO.backend.exception.InvalidTokenException;
import com.aisip.OnO.backend.exception.MissingTokenException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class JwtTokenService {

private final JwtTokenProvider jwtTokenProvider;

/**
* ✅ 유저 정보를 기반으로 새로운 액세스/리프레시 토큰 생성
*/
public TokenResponseDto generateTokens(UserResponseDto user) {
String accessToken = jwtTokenProvider.createAccessToken(user.getUserId());
String refreshToken = jwtTokenProvider.createRefreshToken(user.getUserId());

log.info("AccessToken: {}", accessToken);
log.info("RefreshToken: {}", refreshToken);
log.info("User {} (ID: {}) has logged in", user.getUserName(), user.getUserId());

return new TokenResponseDto(accessToken, refreshToken);
}

/**
* ✅ 액세스 토큰 검증
*/
public void verifyAccessToken(String authorizationHeader) {
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
throw new MissingTokenException("Authorization header missing or invalid");
}

String accessToken = authorizationHeader.substring(7);
if (!jwtTokenProvider.validateToken(accessToken)) {
throw new InvalidTokenException("Invalid or expired access token");
}
}

/**
* ✅ 리프레시 토큰을 이용한 액세스 토큰 갱신
*/
public TokenResponseDto refreshAccessToken(String refreshToken) {
if (!jwtTokenProvider.validateToken(refreshToken)) {
throw new ExpiredTokenException("Invalid or expired refresh token");
}

Long userId = Long.parseLong(jwtTokenProvider.getSubjectFromToken(refreshToken));
String newAccessToken = jwtTokenProvider.createAccessToken(userId);
String newRefreshToken = jwtTokenProvider.createRefreshToken(userId);

return new TokenResponseDto(newAccessToken, newRefreshToken);
}
}

0 comments on commit d5aa617

Please sign in to comment.