[Spring] OAuth2 + JWT 로그인 : (3) JwtAuthenticationFilter의 역할 누락
5. 인증 필터 구현
- JwtAuthenticationFilter를 생성하여 요청 헤더에서 JWT를 추출하고 인증합니다.
Troubleshooting
**<문제>**문제>
AuthController를 완성시키고 비지니스 로직이 만들어졌기 때문에 중간에 login 기능을 테스트 해보기 위해 thymeleaf를 사용해서 프론트를 만들고 Controller로 연결하여 실행시킴.
그러나 해당 url에 빈화면이 보임,,
**<문제 원인="">**문제>
-
JwtAuthenticationFilter
의 역할 누락JwtAuthenticationFilter
는 JWT 토큰을 기반으로 사용자를 인증하는 필터로, 요청 헤더에서 JWT 토큰을 추출하고 이를 검증하여 인증 정보를 설정하는 역할을 합니다. 이 필터가 제대로 동작하지 않으면 Spring Security가 올바르게 인증 및 인가를 처리하지 못하고, 이에 따라 보안 설정이 혼란스러워질 수 있다. -
Spring Security의 필터 체인 문제 Spring Security는 필터 체인을 통해 각 요청을 처리합니다. 특정 필터가 제대로 등록되지 않거나, 인증 및 인가가 필요한 요청을 처리하지 못하면 요청이 거부되거나 화면이 로드되지 않을 수 있습니다. 특히,
JwtAuthenticationFilter
가 없을 때 기본적으로 Security가 모든 요청을 인증 처리해야 한다고 인식할 수 있다. 이 경우 인증되지 않은 요청은 필터를 통과하지 못하고 거부될 수 있다.
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
/login/security/JwtAuthenticationFilter.java 코드
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getParameter("accessToken");
// 1. 요청 헤더에서 Authorization 헤더를 추출
// String token = jwtTokenProvider.resolveToken(request);
// 2. 토큰이 존재하고 유효할 경우, 사용자 정보를 설정
if (token != null && jwtTokenProvider.validateToken(token)) {
if (jwtTokenProvider.isTokenExpired(token)) {
PrintWriter writer = response.getWriter();
writer.print("access token expired");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
// 3. 토큰에서 사용자 정보 추출
Authentication authentication = jwtTokenProvider.getAuthentication(token);
// 4. SecurityContext에 인증 정보 설정
SecurityContextHolder.getContext().setAuthentication(authentication);
}
// 5. 필터 체인에 요청을 넘겨 다음 필터로 처리 진행
filterChain.doFilter(request, response);
}
}
<코드 뜯어보기>
- 토큰에서 추출한 사용자 정보로 Authentication 객체 생성
- SecurityContext에 인증 정보 저장
엥? 아까 CustomOAuth2UserService에서도 인증된 사용자 정보를 SecurityContext에 저장한다고 하지 않았나? JwtAuthenticationFilter에서도 저장하는건가
두 곳 모두 SecurityContext에 인증 정보를 저장하지만 다른 인증 시나리오에서 동작
- CustomOAuth2UserService (소셜 로그인)
- 카카오 로그인 시 최초 인증할 때 실행됨
- OAuth2 인증 후 JWT 토큰을 클라이언트에게 발급
- JwtAuthenticationFilter (API 요청)
- 이후 API 요청때마다 JWT 토큰으로 인증
- 토큰에서 추출한 정보로 SecurityContext 갱신
1. 소셜 로그인 시나리오:
유저 -> 카카오 로그인 -> CustomOAuth2UserService
-> SecurityContext에 인증정보 저장
-> JWT 토큰 발급
2. API 요청 시나리오:
유저 -> API 요청 + JWT 토큰 -> JwtAuthenticationFilter
-> SecurityContext에 인증정보 저장
-> API 처리
이렇게 두 단계로 나누어진 이유는:
- 소셜 로그인은 최초 1회만 필요
- 이후 요청은 JWT로 간단히 인증 처리
- 서버의 무상태성(Stateless) 유지
- API 요청의 효율성 향상