Skip to content

Commit

Permalink
feat: Exception Advisor 생성 및 서비스단 적용 (#24)
Browse files Browse the repository at this point in the history
* refactor: 로그인 요청 DTO 접목

* refactor: Spring Security Exception 생성 및 Response 규격화

* feat: ExceptionAdvisor 및 Custom Exception Handler 생성

* refactor: Service layer에 Exception Handler 적용
  • Loading branch information
Martin0o0 authored Mar 22, 2024
1 parent 9cfed43 commit 8d2ef2d
Show file tree
Hide file tree
Showing 38 changed files with 660 additions and 398 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.goormuniv.ponnect.exception.ErrCode;
import org.goormuniv.ponnect.exception.ErrResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
Expand All @@ -22,12 +24,14 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Acc
throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
Map<String, String> body = new HashMap<>();
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
ErrResponse errResponse = ErrResponse.builder()
.code(ErrCode.INVALID_ACCESS_TOKEN.getCode())
.message(ErrCode.INVALID_ACCESS_TOKEN.getMessage())
.status(ErrCode.INTERNAL_SERVER_ERROR.getStatus())
.build();

body.put("message", "엑세스 권한이 없습니다.");

response.getWriter().write(objectMapper.writeValueAsString(body));
response.getWriter().write(objectMapper.writeValueAsString(errResponse));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

import org.goormuniv.ponnect.exception.ErrCode;
import org.goormuniv.ponnect.exception.ErrResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
Expand All @@ -23,12 +25,13 @@ public void commence(HttpServletRequest request, HttpServletResponse response, A

ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
Map<String, String> body = new HashMap<>();
ErrResponse errResponse = ErrResponse.builder()
.code(ErrCode.UNAUTHORIZED.getCode())
.message(ErrCode.UNAUTHORIZED.getMessage())
.status(ErrCode.UNAUTHORIZED.getStatus()).build();

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
body.put("message", "인증에 실패했습니다.");

response.getWriter().write(objectMapper.writeValueAsString(body));
response.getWriter().write(objectMapper.writeValueAsString(errResponse));

}

Expand Down
12 changes: 6 additions & 6 deletions src/main/java/org/goormuniv/ponnect/auth/JSONLoginFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.goormuniv.ponnect.dto.LoginDto;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
Expand All @@ -17,9 +19,8 @@
import java.util.Map;

public class JSONLoginFilter extends AbstractAuthenticationProcessingFilter {

private static final String CONTENT_TYPE = "application/json";
private static final String USERNAME_KEY = "principal";
private static final String PASSWORD_KEY = "credential";
private static final AntPathRequestMatcher DEFAULT_LOGIN_PATH_REQUEST_MATCHER =
new AntPathRequestMatcher("/api/auth/sign-in", "POST"); //

Expand All @@ -36,13 +37,12 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ


String messageBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
System.out.println(messageBody.toString());
ObjectMapper objectMapper = new ObjectMapper();

Map<String, String> usernamePasswordMap = objectMapper.readValue(messageBody, Map.class);
LoginDto usernamePasswordMap = objectMapper.readValue(messageBody, LoginDto.class);

String email = usernamePasswordMap.get(USERNAME_KEY);
String password = usernamePasswordMap.get(PASSWORD_KEY);
String email = usernamePasswordMap.getPrincipal();
String password = usernamePasswordMap.getCredential();
System.out.println(email + password);


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,13 @@ protected void doFilterInternal(
if (accessToken.isPresent() && SecurityContextHolder.getContext().getAuthentication() == null
&& jwtProvider.validateToken(accessToken.get()) && !redisService.hasKey(accessToken.get())) {
String email = jwtProvider.extractUserEmail(accessToken.get());
try {
UserDetails userDetails = principalDetailsServiceImp.loadUserByUsername(email);
Authentication authentication = jwtProvider.getAuthentication(userDetails); //Authentication 객체 생성

//SecurityContext에 Authentication를 담는다.
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication);
SecurityContextHolder.setContext(securityContext);
}catch (Exception exception){
log.info("jwt 토큰 검증 도중 예외 발생");
}


}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.goormuniv.ponnect.auth;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.goormuniv.ponnect.exception.ErrCode;
import org.goormuniv.ponnect.exception.ErrResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Deprecated
@Slf4j
@Component
public class JwtAuthenticationHandler extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try{
doFilter(request, response, filterChain);
}catch (Exception exception){
log.info("JwtAuthenticationHandler ::{}", exception.getMessage());
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
ErrResponse errResponse = ErrResponse.builder()
.code(ErrCode.INVALID_ACCESS_TOKEN.getCode())
.message(ErrCode.INVALID_ACCESS_TOKEN.getMessage())
.status(ErrCode.INVALID_ACCESS_TOKEN.getStatus())
.build();

response.getWriter().write(objectMapper.writeValueAsString(errResponse));
}
}

}
4 changes: 0 additions & 4 deletions src/main/java/org/goormuniv/ponnect/auth/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ public class JwtProvider implements LogoutHandler {

@PostConstruct
protected void init() {
log.info("secret_key Base64 인코딩시작");
log.info("Original Secret_Key : " + secret_key);
this.secret_key = Base64.getEncoder().encodeToString(secret_key.getBytes(StandardCharsets.UTF_8));
log.info("Encoded Base64 Secret_Key : " + secret_key);
log.info("secretKey 초기화 완료");
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.goormuniv.ponnect.exception.ErrCode;
import org.goormuniv.ponnect.exception.ErrResponse;
import org.json.JSONException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
Expand All @@ -23,13 +25,11 @@ public void onAuthenticationFailure(HttpServletRequest request, HttpServletRespo
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
log.info("로그인 실패함");
Map<String, String> body = new HashMap<>();
try {
body.put("message", "이메일 또는 비밀번호가 잘못되었습니다.");
} catch (JSONException e) {
throw new RuntimeException(e);
}
response.getWriter().write(objectMapper.writeValueAsString(body));
ErrResponse errResponse = ErrResponse.builder()
.code(ErrCode.LOGIN_FAILED.getCode())
.message(ErrCode.LOGIN_FAILED.getMessage())
.status(ErrCode.LOGIN_FAILED.getStatus())
.build();
response.getWriter().write(objectMapper.writeValueAsString(errResponse));
}
}
12 changes: 2 additions & 10 deletions src/main/java/org/goormuniv/ponnect/auth/PrincipalServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,10 @@ public class PrincipalServiceImpl implements UserDetailsService {
private final MemberRepository memberRepository;



@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
public UserDetails loadUserByUsername(String username) {
System.out.println(username);
Member member = Member.builder().build();
try {
member = memberRepository.findByEmail(username).orElseThrow();

} catch (Exception exception) {
log.info("사용자를 찾을 수 없음");
throw new UsernameNotFoundException("사용자를 찾을 수 없습니다.");
}
Member member = memberRepository.findByEmail(username).orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다."));
return PrincipalDetails.builder()
.id(member.getId())
.name(member.getName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,7 @@ public ResponseEntity<?> register (Principal principal,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse
) {
try{
return memberService.register(principal, registerDto, httpServletRequest, httpServletResponse);
}
catch (Exception e){
log.info(e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ErrMsgDto.builder()
.message("가입이 정상적으로 처리되지 않았습니다.")
.statusCode(HttpStatus.BAD_REQUEST.value()).build());
}
}

@Operation(summary="JWT 유효성 확인 및 기본 유저 정보 반환", description = "JWT 유효성 확인 및 기본 유저 정보 반환")
Expand Down
17 changes: 0 additions & 17 deletions src/main/java/org/goormuniv/ponnect/dto/CardListDto.java

This file was deleted.

1 change: 1 addition & 0 deletions src/main/java/org/goormuniv/ponnect/dto/ErrMsgDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import lombok.*;

@Deprecated
@AllArgsConstructor
@NoArgsConstructor
@Getter
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/goormuniv/ponnect/dto/LoginDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.goormuniv.ponnect.dto;

import lombok.*;

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LoginDto {
private String principal;
private String credential;
}
34 changes: 34 additions & 0 deletions src/main/java/org/goormuniv/ponnect/exception/CustomException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.goormuniv.ponnect.exception;

import org.springframework.http.HttpStatus;

public class CustomException extends RuntimeException {


private final ErrCode errCode;

private final HttpStatus httpStatus;


public CustomException(final ErrCode errCode, final HttpStatus status) {
super(errCode.getMessage());
this.errCode = errCode;
this.httpStatus = status;
}
// public CustomException(String message, final ErrCode errCode, final HttpStatus status) {
// super(message);
// this.errCode = errCode;
// this.httpStatus = status;
// }


public HttpStatus getStatus() {
return httpStatus;
}

public ErrCode getErrorCode() {
return errCode;
}


}
48 changes: 48 additions & 0 deletions src/main/java/org/goormuniv/ponnect/exception/ErrCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.goormuniv.ponnect.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;


@Getter
@AllArgsConstructor
public enum ErrCode {
UNAUTHORIZED("P401", "인증에 실패하였습니다.", HttpStatus.UNAUTHORIZED.value()),
INVALID_ACCESS_TOKEN("P007", "JWT가 없습니다.", HttpStatus.FORBIDDEN.value()),
DUPLICATED_EMAIL("P0006", "이미 사용중인 이메일입니다.", HttpStatus.BAD_REQUEST.value()),
LOGIN_FAILED("P001", "아이디 또는 비밀번호가 잘못되었습니다.", HttpStatus.BAD_REQUEST.value()),
SIGN_UP_FAILED("P002", "회원가입에 실패하였습니다.", HttpStatus.NOT_ACCEPTABLE.value()),
REISSUE_FAILED("P003", "비밀번호 재발급에 실패했습니다.", HttpStatus.NOT_ACCEPTABLE.value()),
ALREADY_MEMBER("P004", "이미 가입된 사용자 입니다.", HttpStatus.BAD_REQUEST.value()),
USER_NOT_FOUND("P005", "사용자를 찾을 수 없습니다,", HttpStatus.NOT_FOUND.value()),

ALREADY_FOLLOW("P100", "이미 추가한 사용자입니다.", HttpStatus.BAD_REQUEST.value()),


NO_EXIST_FOLLOW_MEMBER("P101", "해당 회원은 팔로우 된 상태가 아닙니다.", HttpStatus.BAD_REQUEST.value()),
NO_EXIST_CARD("P102","해당 명함이 존재하지 않습니다." , HttpStatus.NOT_FOUND.value()),

SELF_FOLLOW("P103", "자신을 팔로우 할 수 업습니다.", HttpStatus.BAD_REQUEST.value()),
NO_EXIST_CATEGORY("P104", "해당 카테고리를 찾을 수 없습니다.", HttpStatus.NOT_FOUND.value()),
NOT_CONTENT("P105", "추가할 항목이 없습니다.", HttpStatus.NOT_ACCEPTABLE.value()),
NOT_FOUND_MEMBER("P410", "사용자 정보를 찾을 수 없습니다.", HttpStatus.NOT_FOUND.value()),
INTERNAL_SERVER_ERROR("P500", "서버가 요청 처리에 실패하였습니다..", HttpStatus.INTERNAL_SERVER_ERROR.value()),
CREATE_CARD_TO_CATEGORY_FAILED("P106", "명함함 등록에 실패하였습니다.", HttpStatus.NOT_ACCEPTABLE.value()),
RENAME_CATEGORY_FAILED("P107", "카테고리 이름 수정에 실파하였습니다.", HttpStatus.NOT_ACCEPTABLE.value()),
REMOVE_CARD_OF_CATEGORY_FAILED("P108", "카테고리 내 명함 삭제에 실파하였습니다.", HttpStatus.NOT_ACCEPTABLE.value()),
NO_EXIST_CARD_OF_CATEGORY("P109", "해당 카테고리 내 명함을 찾을 수 없습니다.", HttpStatus.NOT_FOUND.value()),
REMOVE_CATEGORY_FAILED("P110", "해당 카테고리를 삭제할 수 없습니다.", HttpStatus.NOT_ACCEPTABLE.value()),
NO_PERMISSION("P406", "접근 권한이 없어 수행할 수 없습니다.", HttpStatus.NOT_ACCEPTABLE.value());





private final String code;
private final String message;
private final int status;



}
25 changes: 25 additions & 0 deletions src/main/java/org/goormuniv/ponnect/exception/ErrResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.goormuniv.ponnect.exception;

import lombok.*;


@Getter
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Builder
public class ErrResponse {

private String code;

private int status;

private String message;

public ErrResponse(ErrCode errCode) {
this.code = errCode.getCode();
this.message = errCode.getMessage();
this.status = errCode.getStatus();
}

}
Loading

0 comments on commit 8d2ef2d

Please sign in to comment.