Skip to content

[Feat] 인증 중앙화로 인한 베이스 코드 변경사항 (#556) #557

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: auth-integration
Choose a base branch
from

Conversation

geniusYoo
Copy link
Contributor

@geniusYoo geniusYoo commented Jun 19, 2025

Related issue 🛠

Work Description ✏️

인증 중앙화로 인해 변경되는 코드들, 노가다 작업 전 베이스 코드를 회의에서 같이 작업했어요.

initial setting

1. 가이드라인 프로젝트 코드 추가

기존 플랫폼 팀에서 제공해주신 가이드라인 코드를 추가했어요.
한 가지 수정 사항이 있다면, jwt를 파싱할 때 Bearer를 substring 하지않고 그대로 사용하도록 변경했어요. (앱팀 클라이언트와 협의 완료)

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtAuthenticationService jwtAuthenticationService;
    private static final String ACCESS_TOKEN_PREFIX = "Bearer ";

    @Override
    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);
        }
        filterChain.doFilter(request, response);
    }

    // Bearer 파싱 없이 Authorization 헤더의 값 그대로 전달
    private String getAuthorizationToken(final HttpServletRequest request) {
        return request.getHeader(HttpHeaders.AUTHORIZATION);
    }
}

2. 에러 코드 및 커스텀 예외 추가

커스텀 예외로는 ClientException, JwkException, JwtException
에러 코드는 아래와 같이 추가했어요.

    // AUTH_CLIENT
    RESPONSE_ERROR("외부 서버 응답 오류", HttpStatus.INTERNAL_SERVER_ERROR),
    COMMUNICATION_ERROR("외부 서버 통신 실패", HttpStatus.INTERNAL_SERVER_ERROR),

    // AUTH_JWK
    JWK_KID_NOT_FOUND("해당 kid에 대한 공개키를 찾을 수 없습니다.", HttpStatus.UNAUTHORIZED),
    JWK_INVALID_FORMAT("JWK 형식이 잘못되어 공개키를 파싱할 수 없습니다.", HttpStatus.BAD_REQUEST),
    JWK_FETCH_FAILED("JWK 서버로부터 키를 가져오지 못했습니다.", HttpStatus.INTERNAL_SERVER_ERROR),

    // AUTH_JWT
    JWT_MISSING_AUTH_HEADER("인증 헤더가 존재하지 않습니다.", HttpStatus.UNAUTHORIZED),
    JWT_PARSE_FAILED("잘못된 형식의 JWT입니다.", HttpStatus.UNAUTHORIZED),
    JWT_INVALID_CLAIMS("JWT의 클레임이 유효하지 않습니다.", HttpStatus.UNAUTHORIZED),
    JWT_VERIFICATION_FAILED("JWT 검증에 실패했습니다.", HttpStatus.UNAUTHORIZED),

3. 응답 dto 추가

플랫폼 internal API로 받아오는 유저 정보 응답 dto와 wrapper를 추가했어요.
wrapper에는 같이 응답으로 오는 status, message를 무시하기 위해 @JsonIgnoreProperties를 사용했어요.

@JsonIgnoreProperties(ignoreUnknown = true)
public record PlatformUserInfoWrapper(
        List<PlatformUserInfoResponse> data
) {}

public record PlatformUserInfoResponse(
        int userId,
        String name,
        String profileImage,
        String birthday,
        String phone,
        String email,
        int lastGeneration,
        List<SoptActivities> soptActivities
) {
    public record SoptActivities(
            int activityId,
            int generation,
            String part,
            String team
    ){
    }
}

4. internal API 엔드포인트

internal API 요청을 위한 FeignClient 엔드포인트를 작성했어요.
플랫폼 팀의 유저 정보 조회 API 명세에 따라, 동적 쿼리 파라미터로 유저 아이디를 추가해 요청할 수 있도록 @QueryMap을 사용했어요.

    @RequestLine("GET /api/v1/users")
    PlatformUserInfoWrapper getPlatformUserInfo(@HeaderMap final Map<String, String> headers,
                                                @QueryMap Map<String, Collection<String>> queryMap);

key changes

1. api changes

  • @AuthenticationPrincipaluserId를 파싱하도록 했어요.
  • 유저 정보 중 userId만을 SecurityContextHolder에 저장하기 때문에, 이름을 얻기 위해 Internal API를 사용하는 코드를 추가해 이름을 받아왔어요.
    @GetMapping("/word")
    public ResponseEntity<FortuneResponse> getFortune(
            @AuthenticationPrincipal Long userId,
            @DateTimeFormat(pattern = "yyyy-MM-dd")
            @RequestParam(name = "todayDate") LocalDate todayDate
    ) {
        return ResponseEntity.ok(
                FortuneResponse.of(
                        fortuneService.getTodayFortuneWordByUserId(userId, todayDate),
                        platformService.getPlatformUserInfoResponse(userId).name()
                )
        );
    }

2. internal API Service

  • 단일, 다중 유저 정보 조회를 위해 createQueryParams로 동적으로 파라미터를 생성해 요청하도록 했어요.
  • 단일 조회의 경우에는 getFirst로 첫번째 것을 가져오도록 했어요.
  • 요청 시에는 x-api-keyx-service-name을 시크릿에서 주입받아 헤더로 세팅하도록 했어요.
public class PlatformService {

    private final PlatformClient platformClient;

    @Value("${external.auth.api-key}")
    private String apiKey;

    @Value("${external.auth.service-name}")
    private String serviceName;

    public PlatformUserInfoResponse getPlatformUserInfoResponse(Long userId) {
        final Map<String, String> headers = createAuthorizationHeader();
        final Map<String, Collection<String>> params = createQueryParams(Collections.singletonList(userId));
        PlatformUserInfoWrapper platformUserInfoWrapper = platformClient.getPlatformUserInfo(headers, params);
        return platformUserInfoWrapper.data().getFirst();
    }

    public List<PlatformUserInfoResponse> getPlatformUserInfosResponse(List<Long> userIds) {
        final Map<String, String> headers = createAuthorizationHeader();
        final Map<String, Collection<String>> params = createQueryParams(userIds);
        PlatformUserInfoWrapper platformUserInfoWrapper = platformClient.getPlatformUserInfo(headers, params);
        return platformUserInfoWrapper.data();
    }

    private Map<String, String> createAuthorizationHeader() {
        Map<String, String> headers = new HashMap<>();
        headers.put("x-api-key", apiKey);
        headers.put("x-service-name", serviceName);
        return headers;
    }

    private Map<String, Collection<String>> createQueryParams(List<Long> userId) {
        Map<String, Collection<String>> queryParams = new HashMap<>();
        for (Long id : userId) {
            queryParams.put("userIds", Collections.singletonList(id.toString()));
        }
        return queryParams;
    }
}

Trouble Shooting ⚽️

Related ScreenShot 📷

Uncompleted Tasks 😅

To Reviewers 📢

Copy link

height bot commented Jun 19, 2025

Link Height tasks by mentioning a task ID in the pull request title or commit messages, or description and comments with the keyword link (e.g. "Link T-123").

💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.

@geniusYoo geniusYoo changed the base branch from dev to auth-integration June 19, 2025 10:07
@geniusYoo geniusYoo added the ✨ Feat 새로운 피쳐 생성 label Jun 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ Feat 새로운 피쳐 생성 size/XL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEAT] 인증 중앙화 작업
2 participants