Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 64 additions & 52 deletions src/main/java/org/sopt/app/common/config/ClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,86 @@
import feign.jackson.*;
import feign.okhttp.OkHttpClient;
import feign.slf4j.Slf4jLogger;

import org.sopt.app.application.meeting.CrewClient;
import org.sopt.app.application.platform.PlatformClient;
import org.springframework.context.annotation.*;
import org.sopt.app.application.playground.PlaygroundClient;
import org.sopt.app.common.external.feign.Feign401ErrorDecoder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.EnableFeignClients;

@Configuration
@EnableFeignClients
public class ClientConfig {

@Value("${makers.playground.server}")
private String playgroundEndPoint;
@Value("${makers.playground.server}")
private String playgroundEndPoint;

@Value("${makers.crew.server}")
private String crewEndPoint;

@Value("${external.auth.url}")
private String platformEndPoint;

@Bean
public PlaygroundClient playgroundClient() {
return Feign.builder()
.client(okHttpClient())
.encoder(encoder())
.decoder(decoder())
.errorDecoder(errorDecoder())
.logger(new Slf4jLogger(PlaygroundClient.class))
.logLevel(feignLoggerLevel())
.target(PlaygroundClient.class, playgroundEndPoint);
}

@Value("${makers.crew.server}")
private String crewEndPoint;
@Bean
public CrewClient crewClient() {
return Feign.builder()
.client(okHttpClient())
.encoder(encoder())
.decoder(decoder())
.errorDecoder(errorDecoder())
.logger(new Slf4jLogger(CrewClient.class))
.logLevel(feignLoggerLevel())
.target(CrewClient.class, crewEndPoint);
}

@Value("${external.auth.url}")
private String platformEndPoint;
@Bean
public PlatformClient platformClient() {
return Feign.builder()
.client(okHttpClient())
.encoder(encoder())
.decoder(decoder())
.errorDecoder(errorDecoder())
.logger(new Slf4jLogger(PlatformClient.class))
.logLevel(feignLoggerLevel())
.target(PlatformClient.class, platformEndPoint);
}

@Bean
public PlaygroundClient playgroundClient() {
return Feign.builder()
.client(okHttpClient())
.encoder(encoder())
.decoder(decoder())
.logger(new Slf4jLogger(PlaygroundClient.class))
.logLevel(feignLoggerLevel())
.target(PlaygroundClient.class, playgroundEndPoint);
}
@Bean
public Encoder encoder() {
return new JacksonEncoder();
}

@Bean
public CrewClient crewClient() {
return Feign.builder()
.client(okHttpClient())
.encoder(encoder())
.decoder(decoder())
.logger(new Slf4jLogger(CrewClient.class))
.logLevel(feignLoggerLevel())
.target(CrewClient.class, crewEndPoint);
}
@Bean
public Decoder decoder() {
return new JacksonDecoder();
}

@Bean
public PlatformClient platformClient() {
return Feign.builder()
.client(okHttpClient())
.encoder(encoder())
.decoder(decoder())
.logger(new Slf4jLogger(CrewClient.class))
.logLevel(feignLoggerLevel())
.target(PlatformClient.class, platformEndPoint);
}
@Bean
public ErrorDecoder errorDecoder() {
return new Feign401ErrorDecoder();
}

@Bean
public Encoder encoder() {
return new JacksonEncoder();
}
@Bean
public Decoder decoder() {
return new JacksonDecoder();
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.HEADERS;
}
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient();
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.HEADERS;
}

@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.sopt.app.common.external.feign;

import org.sopt.app.common.exception.UnauthorizedException;
import org.sopt.app.common.response.ErrorCode;

import feign.Response;
import feign.codec.ErrorDecoder;

public class Feign401ErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultDecoder = new Default();

@Override
public Exception decode(String methodKey, Response response) {
int status = response.status();
if (status == 401) {
return new UnauthorizedException(ErrorCode.UNAUTHORIZED);
}
if (status == 403) {
return new UnauthorizedException(ErrorCode.FORBIDDEN);
}
return defaultDecoder.decode(methodKey, response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.lettuce.core.RedisConnectionException;
import java.util.List;
import lombok.extern.slf4j.Slf4j;

import org.sopt.app.common.exception.UnauthorizedException;
import org.sopt.app.common.response.FailureResponse.FieldError;
import org.springframework.http.*;
import org.springframework.validation.*;
Expand Down Expand Up @@ -66,4 +68,11 @@ protected ResponseEntity<FailureResponse> handleRedisConnectionException(final R
final FailureResponse response = FailureResponse.of(ErrorCode.REDIS_CONNECTION_ERROR, errors);
return new ResponseEntity<>(response, HttpStatus.SERVICE_UNAVAILABLE);
}

@ExceptionHandler(UnauthorizedException.class)
protected ResponseEntity<FailureResponse> handleUnauthorizedException(final UnauthorizedException e) {
final ErrorCode code = e.getErrorCode(); // 전달된 코드 사용
final HttpStatus status = code.getHttpStatus(); // 코드가 가진 status 사용 (401/403 등)
return new ResponseEntity<>(FailureResponse.of(code), status);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.sopt.app.common.security.filter;

import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.IOException;
Expand All @@ -8,9 +9,9 @@
import lombok.RequiredArgsConstructor;
import org.sopt.app.common.exception.JwtException;
import org.sopt.app.common.jwt.service.JwtAuthenticationService;
import org.sopt.app.common.response.ErrorCode;
import org.sopt.app.common.security.authentication.MakersAuthentication;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
Expand All @@ -26,16 +27,23 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain)
throws ServletException, IOException {

String authorizationToken = getAuthorizationToken(request);
if (authorizationToken != null) {
MakersAuthentication authentication = jwtAuthenticationService.authenticate(authorizationToken);
authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(authentication);
@NonNull FilterChain filterChain
) throws ServletException, IOException {
try {
String authorizationToken = getAuthorizationToken(request);
if (authorizationToken != null) {
MakersAuthentication authentication = jwtAuthenticationService.authenticate(authorizationToken);
authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
} catch (ExpiredJwtException e) {
SecurityContextHolder.clearContext();
throw new BadCredentialsException("JWT token expired", e);
} catch (JwtException e) {
SecurityContextHolder.clearContext();
throw new BadCredentialsException("Invalid JWT token", e);
}
filterChain.doFilter(request, response);
}

private String getAuthorizationToken(final HttpServletRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.sopt.app.common.exception.UnauthorizedException;
import org.sopt.app.common.response.*;
import org.springframework.http.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

Expand All @@ -28,6 +29,9 @@ protected void doFilterInternal(HttpServletRequest httpServletRequest,
ErrorCode errorMessage = e.getErrorCode();
HttpStatus httpStatus = errorMessage.getHttpStatus();
setResponse(httpServletResponse, httpStatus, errorMessage);
} catch (AuthenticationException e) {
// JwtAuthenticationFilter에서 BadCredentialsException으로 래핑된 경우
setResponse(httpServletResponse, HttpStatus.UNAUTHORIZED, ErrorCode.TOKEN_EXPIRED);
}
}

Expand Down