프로그래밍/spring

스프링 시큐리티 OAuth2 loadUser 호출이 안될때

승민아 2024. 7. 23. 15:43

문제 상황

기존에 카카오 로그인을 RestTemplate를 이용해 REST API로 구현을 했었는데

Spring Security의 OAuth2를 이용해 손쉽게 카카오 로그인 API 결과를 얻을 수 있다.

 

그런데 이상하게 AccessToken을 얻은 다음 이뤄지는 loadUser 메소드가 동작하지 않는것이다.

 

SecurityConfig.class

http.oauth2Login((auth2) -> auth2
    .userInfoEndpoint(userInfo -> userInfo
        .userService(customOAuthService))
);

사용자 정보를 가져온 후 처리하는 customOAuthService를 설정해주었는데

 

package com.example.bucketlist.config.security;

import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class CustomOAuthUserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        System.out.println("CustomOAuthUserService.loadUser");
        ...
    }

}

 

loadUser가 호출되지 않는 것이다.

 

토큰 받기에 문제가 있는지 확인을 위해 인가 코드는 잘 넘어오는지

다음과 같이 컨트롤러 코드를 작성했고 잘 넘어오는것은 확인된다.

@GetMapping("/oauth2/code/kakao")
public String signinKakao(@RequestParam String code) {
    System.out.println("code = " + code);
    return "test";
}

 

인가 코드 출력

 

이상하게 인가 코드는 잘 넘어가는데 토큰 발급 로직은 동작하지 않는것이다.

 

문제 원인 및 해결

OAuth2LoginAuthenticationFilter를 보자.

public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";
    ...
}

DEFAULT_FILTER_PROCESSES_URI의 기본값은 "/login/oauth2/code/*"로 되어있다.

 

이 경로는 OAuth2 로그인을 처리하는 엔드 포인트로 인가 코드가 전달되는 경로를 뜻한다.

 

이 값을 따로 내가 설정해주지 않았기 때문에 기본값으로 설정되어 있는 것이다.

 

이것이 기본값이면 무슨 문제가 있을까?

OAuth2LoginAuthenticationFilter가 상속 받은 AbstractAuthenticationProcessingFilter를 보자.

 

AbstractAuthenticationProcessingFilter의 필터 로직을 보면

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
    if (!requiresAuthentication(request, response)) {
        chain.doFilter(request, response);
        return;
    }
    
    ... successfulAuthentication 호출 과정
    
}

requiresAuthentication의 반환값에 따라

Authentication 로직을 호출할지말지가 결정된다.

 

requiresAuthentication는 무슨 메소드일까?

protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
    if (this.requiresAuthenticationRequestMatcher.matches(request)) {
        return true;
    }
    if (this.logger.isTraceEnabled()) {
        this.logger
            .trace(LogMessage.format("Did not match request to %s", this.requiresAuthenticationRequestMatcher));
    }
    return false;
}

request의 경로가 설정한 경로와 일치하는지 검증한다.

 

OAuth2LoginConfigurer

OAuth2LoginConfigurer를 보자. 여기에 loginProcessingUrl이 기본값으로 DEFAULT_FILTER_PROCESS_URI를 사용하고있다.

우리는 이것을 변경해서 OAuth2 인가 코드를 처리할 경로를 설정해주면 되는 것이다.

 

SecurityConfig.class

http.oauth2Login((auth2) -> auth2
    .loginProcessingUrl("/members/signin/*")
);

loginProcessingUrl을 설정하여 인가 코드를 처리할 수 있도록 해주자.

 

출력

드디어 카카오 OAuth2 로그인 성공 후 처리하는 loadUser 메소드가 실행이 된다.

 

임시로 데이터를 넣은 OAuth2User가 잘 반환된다.!