Skip to content

Conversation

@Jinho622
Copy link
Member

#️⃣연관된 이슈

#246

📝작업 내용

작업한 내용을 작성해주세요.
관리자페이지(위치정보시스템) 로그인 실패 문제 해결

🔎코드 설명

코드에 대한 설명을 작성해주세요.

  • 오류 내용: 'Cannot invoke String.equals(Object) because 'accessToken' is null'
  • 오류 원인: JwtAuthenticationFilter에서 블랙리스트 체크 시 isTokenBlacklisted()에 Authorization 헤더 값을 전달하고 있었음. 쿠키 기반 인증( resolveToken() )으로 accessToken을 가져오는 경우 authHeader가 null이 되어 NPE 발생 → 인증 실패
  • 오류 해결: resolveToken()으로 얻은 accessToken을 사용하도록 변경하여 isTokenBlacklisted() 호출 시 null 전달이 발생하지 않도록 수정

💬고민사항 및 리뷰 요구사항 (Optional)

x

비고 (Optional)

x

- 오류 내용: 'Cannot invoke String.equals(Object) because 'accessToken' is null'
- 오류 원인: JwtAuthenticationFilter에서 블랙리스트 체크 시 isTokenBlacklisted()에 Authorization 헤더 값을 전달하고 있었음. 쿠키 기반 인증( resolveToken() )으로 accessToken을 가져오는 경우 authHeader가 null이 되어 NPE 발생 → 인증 실패
- 오류 해결: resolveToken()으로 얻은 accessToken을 사용하도록 변경하여 isTokenBlacklisted() 호출 시 null 전달이 발생하지 않도록 수정
@Jinho622 Jinho622 requested a review from a team December 19, 2025 16:09
@Jinho622 Jinho622 self-assigned this Dec 19, 2025
@Jinho622 Jinho622 added the 🐞 BugFix Something isn't working label Dec 19, 2025
@gemini-code-assist
Copy link

Summary of Changes

Hello @Jinho622, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 관리자 페이지 로그인 실패 문제를 해결합니다. 기존에는 JWT 블랙리스트 확인 과정에서 NullPointerException이 발생하여 인증에 실패하는 문제가 있었습니다. 이는 Authorization 헤더 처리와 블랙리스트 저장 로직의 불일치에서 비롯되었으며, 본 변경사항은 토큰 처리 로직을 개선하여 안정적인 로그인 및 로그아웃 기능을 보장합니다.

Highlights

  • JWT 블랙리스트 확인 로직 수정: JwtAuthenticationFilter에서 블랙리스트 확인 시 Authorization 헤더 대신 resolveToken()으로 얻은 accessToken을 사용하도록 변경하여 NullPointerException을 해결했습니다.
  • resolveToken 메서드 개선: JwtAuthenticationFilter의 resolveToken 메서드에서 Authorization 헤더가 null일 경우 startsWith("Bearer ") 호출로 인한 NullPointerException을 방지하기 위해 조건문을 추가했습니다.
  • 로그아웃 시 블랙리스트 저장 방식 변경: UserCommandServiceImpl의 logout 메서드에서 accessToken을 블랙리스트에 저장할 때 "Bearer " 접두사를 제거한 순수 토큰 값만 저장하도록 수정했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

관리자 페이지 로그인 실패 문제를 해결하기 위한 수정을 확인했습니다. JwtAuthenticationFilter에서 isTokenBlacklisted 호출 시 authHeader 대신 accessToken을 사용하도록 변경하여 NullPointerException을 해결한 것은 올바른 접근입니다. 또한 logout 시 블랙리스트에 토큰만 저장하도록 변경하여 일관성을 맞춘 점도 좋습니다.

코드 리뷰 중 몇 가지 개선점을 발견했습니다. JwtAuthenticationFilter에서 불필요한 중첩 if문을 간소화하고, isTokenBlacklisted 메서드의 심각한 성능 문제를 해결할 방안을 제안했습니다. 또한 UserCommandServiceImpllogout 메서드에서 토큰 파싱 시 발생할 수 있는 예외를 방지하기 위한 코드 수정을 제안했습니다. 해당 내용들을 확인하고 반영해주시면 코드의 안정성과 성능이 크게 향상될 것입니다.

String accessToken = resolveToken(request);
jwtUtils.validateToken(accessToken); // 토큰 검증
jwtUtils.isTokenBlacklisted(authHeader); // 🚨 블랙리스트 확인
jwtUtils.isTokenBlacklisted(accessToken); // 🚨 블랙리스트 확인

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

현재 isTokenBlacklisted 구현은 Redis의 KEYS 명령을 사용하여 운영 환경에서 심각한 성능 문제를 일으킬 수 있습니다. KEYS는 전체 키를 스캔하므로 사용을 피해야 합니다.

블랙리스트 확인을 위해 토큰에서 userId를 추출하고, blackList:{userId} 키를 직접 조회하여 토큰을 비교하는 것이 훨씬 효율적입니다. validateToken이 이미 호출되었으므로, 반환된 클레임에서 userId를 가져와 사용할 수 있습니다. 이 로직을 수정하여 성능 문제를 해결하는 것을 강력히 권장합니다.


@Override
public void logout(String accessToken) {
String accessTokenWithoutBearer = accessToken.split(" ")[1];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

accessToken.split(" ")[1]accessToken이 "Bearer "로 시작하지 않거나 공백을 포함하지 않는 경우 ArrayIndexOutOfBoundsException을 발생시킬 수 있습니다. 토큰을 추출하기 전에 형식을 확인하고 substring을 사용하는 것이 더 안전합니다.

Suggested change
String accessTokenWithoutBearer = accessToken.split(" ")[1];
if (accessToken == null || !accessToken.startsWith("Bearer ")) {
throw new TtattaBackend.ttatta.jwt.exception.CustomJwtException("올바르지 않은 토큰 형식입니다.");
}
String accessTokenWithoutBearer = accessToken.substring("Bearer ".length());

Comment on lines +166 to 171
if (authHeader != null) {
if (authHeader.startsWith("Bearer ")){
checkAuthorizationHeader(authHeader); // header 가 올바른 형식인지 체크
return JwtUtils.getTokenFromHeader(authHeader);
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

가독성을 위해 중첩 if 문을 사용하는 대신, 논리 AND 연산자(&&)를 사용하여 하나의 if 문으로 결합하는 것이 더 좋습니다. 이전 코드가 더 간결하고 읽기 쉬웠습니다.

        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            checkAuthorizationHeader(authHeader);   // header 가 올바른 형식인지 체크
            return JwtUtils.getTokenFromHeader(authHeader);
        }

@Jinho622 Jinho622 merged commit 9c9ca1e into develop Dec 19, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 BugFix Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fix] 관리자페이지(위치정보시스템) 로그인 실패 문제 해결

2 participants