Skip to content

Commit

Permalink
refactor: CommonException 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
jemin committed Apr 8, 2024
1 parent 47226ae commit 9854d2f
Show file tree
Hide file tree
Showing 56 changed files with 212 additions and 287 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,10 @@
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.Logger;

@EnableFeignClients
@ImportAutoConfiguration({FeignAutoConfiguration.class})
@Configuration
public class FeignConfig {

@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@

import cmc.mellyserver.clientauth.api.LoginClientResult;

/*
현재 Melly에서는 카카오, 네이버, 구글, 애플 OAuth를 사용하고 있습니다. 차후 OAuth는 얼마든지 추가될 수 있기에 확장성을 고려한 설계가 필요합니다.
따라서 LoginClient 인터페이스를 만들어서 구체 클라이언트들이 구현하도록 만들었습니다.
OAuth 리소스 서버로부터 데이터를 받아와야 하는 쪽에서는 누구에게 요청하는지 관심을 가지지 않아도 됩니다.
- support : 해당 클라이언트가 어떤 Provider인지 판별합니다
- getUserData : 실제 유저 데이터를 받아옵니다
*/
public interface LoginClient {

boolean supports(String provider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
import cmc.mellyserver.clientauth.LoginClient;
import lombok.RequiredArgsConstructor;

/*
타 모듈은 Client 호출이 필요할때 xxClient 객체를 사용해서 호출하면 됩니다.
OpenFeign 인터페이스에 대한 접근은 패키지 내부로 숨겼습니다.
*/
@Component
@RequiredArgsConstructor
public class NaverClient implements LoginClient {
Expand Down
32 changes: 6 additions & 26 deletions clients/client-auth/src/main/resources/client-auth-local.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
client:
oauth:
uri: # OAuth2 리소스 서버 주소
uri:
kakao: https://kapi.kakao.com/v2/user/me
naver: https://openapi.naver.com/v1/nid/me
google: https://oauth2.googleapis.com/tokeninfo
Expand All @@ -10,30 +10,10 @@ spring.cloud.openfeign:
client:
config:
default:
connectTimeout: 3000 # SYN InitialRTO 1초와 SYN/ACK InitialRTO 1초를 고려해서 3초로 설정
readTimeout: 5000 # 외부 인증 서비스 고려해서 넉넉히 2초로 설정
loggerLevel: full # openFeign 관련 모든 로그 출력
connectTimeout: 3000
readTimeout: 3000
loggerLevel: full

httpclient:
max-connections: 400 # 전체 커넥션 수
max-connections-per-route: 100 # Route당 할당되는 최대 커넥션 수

circuitbreaker:
enabled: true

resilience4j.circuitbreaker:
configs:
default:
slidingWindowType: COUNT_BASED
minimumNumberOfCalls: 3
slidingWindowSize: 5
waitDurationInOpenState: 5s
failureRateThreshold: 80
slowCallDurationThreshold: 2000
slowCallRateThreshold: 80
permittedNumberOfCallsInHalfOpenState: 5
automaticTransitionFromOpenToHalfOpenEnabled: true
eventConsumerBufferSize: 10
instances:
default:
baseConfig: default
max-connections: 400
max-connections-per-route: 100
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.springframework.stereotype.Repository;

import cmc.mellyserver.auth.service.dto.request.EmailCertificationRequest;
import cmc.mellyserver.support.exception.BusinessException;
import cmc.mellyserver.support.exception.CommonException;
import cmc.mellyserver.support.exception.ErrorCode;
import lombok.RequiredArgsConstructor;

Expand All @@ -28,7 +28,7 @@ public void sendCertification(String email) {
public void verify(EmailCertificationRequest requestDto) {

if (isVerify(requestDto)) {
throw new BusinessException(ErrorCode.NOT_VALID_ERROR);
throw new CommonException(ErrorCode.NOT_VALID_ERROR);
}

certificationNumberDao.delete(requestDto.email());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,27 @@
import com.fasterxml.jackson.databind.ObjectMapper;

import cmc.mellyserver.support.exception.ErrorCode;
import cmc.mellyserver.support.exception.LogoutOrWithdrawException;
import cmc.mellyserver.support.response.ErrorResponse;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class JwtExceptionFilter extends OncePerRequestFilter {

private final ObjectMapper mapper;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try {
chain.doFilter(request, response);
} catch (JwtException ex) {
setExpiredErrorResponse(response);
} catch (LogoutOrWithdrawException ex) {
setLogoutOrWithdrawErrorResponse(response);
}
}

Expand All @@ -39,21 +40,6 @@ private void setExpiredErrorResponse(HttpServletResponse response) throws IOExce
response.setStatus(HttpStatus.OK.value());

ErrorResponse error = ErrorResponse.of(ErrorCode.EXPIRED_TOKEN);

ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), error);

}

private void setLogoutOrWithdrawErrorResponse(HttpServletResponse response) throws IOException {

response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpStatus.OK.value());

ErrorResponse error = ErrorResponse.of(ErrorCode.LOGOUT_WITHDRAW_USER);

ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), error);
}

}
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package cmc.mellyserver.auth.common.filter;

import java.io.IOException;
import java.util.Objects;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import cmc.mellyserver.auth.common.util.HeaderUtil;
import cmc.mellyserver.auth.token.TokenProvider;
import cmc.mellyserver.support.exception.LogoutOrWithdrawException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -26,8 +22,6 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {

private final TokenProvider tokenProvider;

private final RedisTemplate redisTemplate;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Expand All @@ -36,23 +30,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse

if (StringUtils.hasText(accessToken) && tokenProvider.validateToken(accessToken)) {

// checkLogoutOrWithdrawUser(accessToken);

Authentication authentication = tokenProvider.getAuthentication(accessToken);

SecurityContextHolder.getContext().setAuthentication(authentication);
}

filterChain.doFilter(request, response);
}

private void checkLogoutOrWithdrawUser(String accessToken) {

ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

if (Objects.nonNull(valueOperations.get(accessToken))) {
throw new LogoutOrWithdrawException("already Logout or withdraw user");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
Expand All @@ -28,9 +27,7 @@
public class SecurityConfig {

private final JwtTokenProvider jwtTokenProvider;

private final RedisTemplate redisTemplate;


private final RestAuthenticationEntryPoint authenticationEntryPoint;

private final TokenAccessDeniedHandler accessDeniedHandler;
Expand All @@ -40,7 +37,7 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {

TokenAuthenticationFilter authenticationFilter = new TokenAuthenticationFilter(jwtTokenProvider, redisTemplate);
TokenAuthenticationFilter authenticationFilter = new TokenAuthenticationFilter(jwtTokenProvider);

httpSecurity.csrf(CsrfConfigurer::disable)
.cors(CorsConfigurer::disable)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package cmc.mellyserver.auth.service;

import java.util.Objects;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
Expand All @@ -16,7 +18,7 @@
import cmc.mellyserver.dbcore.user.User;
import cmc.mellyserver.domain.user.UserReader;
import cmc.mellyserver.domain.user.UserWriter;
import cmc.mellyserver.support.exception.BusinessException;
import cmc.mellyserver.support.exception.CommonException;
import cmc.mellyserver.support.exception.ErrorCode;
import lombok.RequiredArgsConstructor;

Expand Down Expand Up @@ -49,11 +51,6 @@ public TokenResponseDto signup(AuthSignupRequestDto authSignupRequestDto) {
return TokenResponseDto.of(tokenDto.accessToken(), tokenDto.refreshToken().token());
}

/*
로그인 요청이 몰리는 상황에서 TPS를 올릴 수 있는 방법들을 고민했습니다.
- 1. email 컬럼에 대한 인덱스를 생성해서 DB 랜덤 I/O 시간 단축
- 2. password 비교하는 과정에서 encoder의 암호화 강도가 높아서 CPU 사용량과 처리시간 증가, EC2의 CPU 스펙에 맞춰서 암호화 강도 조절
*/
@Transactional
public TokenResponseDto login(AuthLoginRequestDto authLoginRequestDto) {

Expand All @@ -66,7 +63,6 @@ public TokenResponseDto login(AuthLoginRequestDto authLoginRequestDto) {
return TokenResponseDto.of(tokenDto.accessToken(), tokenDto.refreshToken().token());
}

// Refresh Token Rotation (RTR) 전략 적용
public TokenResponseDto reIssueAccessTokenAndRefreshToken(final String token) {

Long userId = tokenService.extractUserId(token);
Expand Down Expand Up @@ -100,22 +96,26 @@ public void withdraw(final Long userId, final String accessToken) {
public void checkDuplicatedNickname(final String nickname) {

if (userReader.existsByNickname(nickname)) {
throw new BusinessException(ErrorCode.DUPLICATE_NICKNAME);
throw new CommonException(ErrorCode.DUPLICATE_NICKNAME);
}
}

public void checkDuplicatedEmail(final String email) {

if (userReader.findByEmail(email).isPresent()) {
throw new BusinessException(ErrorCode.DUPLICATE_EMAIL);
if (Objects.nonNull(userReader.findByEmail(email))) {
throw new CommonException(ErrorCode.DUPLICATE_EMAIL);
}
}

@Transactional
public void updateForgetPassword(ChangePasswordRequest requestDto) {

User user = userReader.findByEmail(requestDto.email())
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
User user = userReader.findByEmail(requestDto.email());

if (Objects.isNull(user)) {
throw new CommonException(ErrorCode.USER_NOT_FOUND);
}

user.changePassword(requestDto.passwordAfter());
}

Expand All @@ -128,29 +128,33 @@ public void changePassword(final Long userId, ChangePasswordRequest requestDto)
String passwordAfter = passwordEncoder.encode(requestDto.passwordAfter());

if (!userReader.existsByEmailAndPassword(user.getEmail(), passwordBefore)) {
throw new BusinessException(ErrorCode.BEFORE_PASSWORD_NOT_EXIST);
throw new CommonException(ErrorCode.BEFORE_PASSWORD_NOT_EXIST);
}

user.changePassword(passwordAfter);
}

private User checkEmail(final String email) {
return userReader.findByEmail(email).orElseThrow(() -> {
throw new BusinessException(ErrorCode.INVALID_EMAIL);
});
User byEmail = userReader.findByEmail(email);

if (Objects.isNull(byEmail)) {
throw new CommonException(ErrorCode.INVALID_EMAIL);
}

return byEmail;
}

private void checkPassword(final String password, final String originPassword) {
if (!passwordEncoder.matches(password, originPassword)) {
throw new BusinessException(ErrorCode.INVALID_PASSWORD);
throw new CommonException(ErrorCode.INVALID_PASSWORD);
}
}

private void checkAbnormalUserAccess(final String token, final Long userId, final RefreshToken refreshToken) {

if (!refreshToken.refreshToken().equals(token)) {
tokenService.removeRefreshToken(userId);
throw new BusinessException(ErrorCode.ABNORMAL_ACCESS);
throw new CommonException(ErrorCode.ABNORMAL_ACCESS);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import cmc.mellyserver.auth.service.dto.response.RefreshTokenDto;
import cmc.mellyserver.dbcore.user.User;
import cmc.mellyserver.support.exception.BusinessException;
import cmc.mellyserver.support.exception.CommonException;
import cmc.mellyserver.support.exception.ErrorCode;
import lombok.RequiredArgsConstructor;

Expand All @@ -26,7 +26,7 @@ public TokenDto createToken(User user) {

public RefreshToken findRefreshToken(Long userId) {
return authTokenDao.findRefreshToken(userId).orElseThrow(() -> {
throw new BusinessException(ErrorCode.RELOGIN_REQUIRED);
throw new CommonException(ErrorCode.RELOGIN_REQUIRED);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

public abstract class LockMessage {

// Optimistic Lock
public static final String OPTIMISTIC_LOCK_AOP_ENTRY = "Optimistic Lock AOP 진입";

public static final String OPTIMISTIC_LOCK_RETRY = "optimistic Lock acquire fail, retry start";
Expand Down
Loading

0 comments on commit 9854d2f

Please sign in to comment.