프로젝트

[Spring Security] 인가 실패 시 카카오 로그인 화면 리턴 오류

SY 키키 2024. 5. 17. 20:16

스프링 시큐리티를 이용하여 인증/인가 로직을 작성하며 인증은 성공했지만, 인가는 실패한 경우 카카오 로그인 화면이 리턴되는 오류가 발생하였습니다.

 

다음은 제 SecurityConfig.java 파일입니다.

// Spring Security 설정
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                ...
                .authorizeHttpRequests(
                        authorize -> authorize
                                .requestMatchers("/store/join", "/user/login", "/user/reissue", "/customer/join", "/login/oauth2/**").permitAll()
                                .requestMatchers("/user/logout", "/customer/info").authenticated()
                                .requestMatchers("/coupon", "/coupon/stop/**", "/coupon/finish").hasAuthority(UserType.STORE.getName())
                                .requestMatchers("/coupon/issue","/coupon/**").hasAuthority(UserType.CUSTOMER.getName())
                                .anyRequest().authenticated()
                )
                .oauth2Login((oauth2) -> {
                    oauth2
                            .userInfoEndpoint((endpoint) -> endpoint.userService(customOAuth2UserService));
                    oauth2.successHandler(oAuth2AuthenticationSuccessHandler);
                    oauth2.failureHandler(oAuth2AuthenticationFailureHandler);
                })
        ;

        return http.build();
    }
}

 

예를 들어서, "/coupon" 이라는 API를 CUSTOMER 유저가 보내도록 해보겠습니다. 그렇게 되면 예상 로직은 JwtFilter를 통해 인증단계를 거친 후, SpringSecurity를 내 필터를 통해 인가 작업을 합니다. 이 때, 인가가 되지 않기 때문에 403에러를 발생시켜야 합니다.

 

하지만 제 리턴 결과는 다음과 같았습니다.

<!doctype html>
<html lang="ko">

<head>
    <meta charset="utf-8">
    <meta name="viewport"
        content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width">
    <meta name="next-head-count" content="2">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta content="website" property="og:type">
    <meta content="카카오계정" property="og:title">
    <meta content="https://accounts.kakaocdn.net/images/og_kakao.png" property="og:image">
    <script type="text/javascript" src="//t1.daumcdn.net/tiara/js/v1/tiara.min.js" defer></script>
    <link rel="preload" href="https://accounts.kakaocdn.net/_next/static/css/c02e46991717addb.css" as="style">
    <link rel="stylesheet" href="https://accounts.kakaocdn.net/_next/static/css/c02e46991717addb.css" data-n-g="">
    <link rel="preload" href="https://accounts.kakaocdn.net/_next/static/css/66a727ed600e6865.css" as="style">
    <link rel="stylesheet" href="https://accounts.kakaocdn.net/_next/static/css/66a727ed600e6865.css" data-n-p="">
    <link rel="preload" href="https://accounts.kakaocdn.net/_next/static/css/8493bcbffbf0b5be.css" as="style">
    <link rel="stylesheet" href="https://accounts.kakaocdn.net/_next/static/css/8493bcbffbf0b5be.css" data-n-p="">
    <noscript data-n-css=""></noscript>
    <script defer nomodule="" src="https://accounts.kakaocdn.net/_next/static/chunks/polyfills-5cd94c89d3acac5f.js">
    </script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/webpack-c518d6630e528dd6.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/framework-f8115f7fae64930e.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/main-a7d45bce11193232.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/pages/_app-91ac83647fe8a97f.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/29107295-f5d3d9a71e7e292a.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/70368-58b0a348debb6a04.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/69695-9ee32dd2295c9b6f.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/74534-ef40f6ad4c13b9c2.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/35999-9285c692bdc44fb2.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/3889-0f1ee3f4696c083c.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/chunks/pages/login/login-2c5b9572ffda1957.js" defer>
    </script>
    <script src="https://accounts.kakaocdn.net/_next/static/zEAT7DHzOrUgp8stG7ZY8/_buildManifest.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/zEAT7DHzOrUgp8stG7ZY8/_ssgManifest.js" defer></script>
    <script src="https://accounts.kakaocdn.net/_next/static/zEAT7DHzOrUgp8stG7ZY8/_middlewareManifest.js" defer>
    </script>
</head>

<body class="os_other pc type_responsive">
    <div id="__next" data-reactroot=""></div>
    <script id="__NEXT_DATA__" type="application/json">
        {"props":{"pageProps":{"pageContext":{"commonContext":{"locale":"ko","uaClass":"os_other  pc","responsiveView":true,"responsivePopup":false,"mobile":false,"webview":{"app":"web","webViewType":"none","appVersion":"","os":"other","osVersion":"","supportFilePicker":true,"supportExecUrlScheme":false,"supportMarketUrlScheme":true,"supportNavigation":false},"supportRefererMetaTag":false,"showHeader":false,"showFooter":true,"linkParams":{},"showDarkMode":null,"_csrf":"c569cdca-ec63-4648-aed2-f96e17b86fea","kage_file_max_size":100,"upload_kage_url":"https://up-api1-kage.kakao.com/up/kaccount-p/","p":"LMgrlTFOBaD0RT7kpjUd24G-V4p0FAoXSZj0rYezd2Q"},"context":{"webType":"web","defaultEmail":null,"showStaySignIn":true,"defaultStaySignIn":false,"appendStaySignedIn":false,"defaultCountryCode":"KR_82","showQrLogin":true,"showWebTalkLogin":false,"showDeviceFormLogin":false,"needCaptcha":false,"showIpSecurity":false,"loginUrl":"/login?continue=https%3A%2F%2Fkauth.kakao.com%2Foauth%2Fauthorize%3Fscope%3Dprofile_nickname%2520profile_image%2520account_email%26response_type%3Dcode%26state%3DtGLD1-U9H-Hz74gNTdALLzXFo-_ZUdYlO3kTYyUw3Ok%253D%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8080%252Flogin%252Foauth2%252Fcode%252Fkakao%26through_account%3Dtrue%26client_id%3D56e7a5438fdb08baa0c60c5e42ff7a62","continueUrl":"https://kauth.kakao.com/oauth/authorize?scope=profile_nickname%20profile_image%20account_email&response_type=code&state=tGLD1-U9H-Hz74gNTdALLzXFo-_ZUdYlO3kTYyUw3Ok%3D&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Flogin%2Foauth2%2Fcode%2Fkakao&through_account=true&client_id=56e7a5438fdb08baa0c60c5e42ff7a62","useSimpleLogin":true,"exceedSimpleLoginLimit":false,"defaultSaveSignIn":false,"isTalkLoginError":false,"linkParams":{"lang":["ko"]},"requests":{"check_daum_sso":["get","https://logins.daum.net/accounts/endpoint/favicon.ico"]},"bannerImageUrl":null}}}},"page":"/login/login","query":{},"buildId":"zEAT7DHzOrUgp8stG7ZY8","assetPrefix":"https://accounts.kakaocdn.net","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}
    </script>
</body>

</html>

 

또한, HttpStatus도 200이 나왔습니다. 이를 통해, API에서 인가가 되지 않는다면, 소셜로그인 필터가 실행됨을 확인할 수 있었습니다.

 

이는 정상로직이 아니기 때문에 소셜로그인이 아닌, 403 Forbidden을 리턴하도록 해야합니다.

 

해결방안

// Spring Security 설정
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

 
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                ...
                .authorizeHttpRequests(
                        authorize -> authorize
                                .dispatcherTypeMatchers(DispatcherType.ERROR).permitAll() // 인가 안되면 자체 시큐리티 타는데 -> 이거 막아주는 로직
                                .requestMatchers("/store/join", "/user/login", "/user/reissue", "/customer/join", "/login/oauth2/**").permitAll()
                                .requestMatchers("/user/logout", "/customer/info").authenticated()
                                .requestMatchers("/coupon", "/coupon/stop/**", "/coupon/finish").hasAuthority(UserType.STORE.getName())
                                .requestMatchers("/coupon/issue","/coupon/**").hasAuthority(UserType.CUSTOMER.getName())
                                .anyRequest().authenticated()
                )
                ...
        ;

        return http.build();
    }
}

 

다음과 같이 .authorizeHttpRequests 의 requestMatchers 상단에 다음 코드를 추가해주면 됩니다.

.dispatcherTypeMatchers(DispatcherType.ERROR).permitAll()​

 

그러면 결과가 정상적으로 나오는 것을 확인할 수 있습니다.

{
    "timestamp": "2024-05-17T11:21:04.573+00:00",
    "status": 403,
    "error": "Forbidden",
    "message": "Forbidden",
    "path": "/coupon"
}