04.15 TIL (서버)

전날(14일) AWS에서 CI/CD를 하다 눈을 떠보니 로그인하니 CORS가 나왔다. 분명히 CORS 처리를 했으므로… 코드를 다시 살펴보기 시작했습니다.

package com.sparta.petplace.config;

import com.sparta.petplace.auth.jwt.JwtAuthFilter;
import com.sparta.petplace.auth.jwt.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;

import java.util.Arrays;
import java.util.List;

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
@EnableGlobalMethodSecurity(securedEnabled = true) // @Secured 어노테이션 활성화
public class WebSecurityConfig {
    private final JwtUtil jwtUtil;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        // h2-console 사용 및 resources 접근 허용 설정
        return (web) -> web.ignoring()
                // .requestMatchers(PathRequest.toH2Console())
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }


    //  Spring Security 필터 체인을 구성하는 인터페이스다.
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // CORS 문제 해결
        http.cors().configurationSource(request -> {
            CorsConfiguration cors = new CorsConfiguration();
            //  모든 패턴 허용
            cors.setAllowedOriginPatterns(List.of("*"));
            //  API 메서드 허용 범위
            cors.setAllowedMethods(Arrays.asList("GET","POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
            //  Headers 모든 값
            cors.setAllowedHeaders(List.of("*"));
            //  header token "Authorization"값 허용
            cors.addExposedHeader("Authorization");
            //  header token "Refresh_Token"값 허용
            cors.addExposedHeader("RefreshToken");
            //  내 서버가 응답할 때 json을 JS에서 처리할 수 있게 하려면 설정 (허용하려면 true)
            //  사용자 자격증명과 함께 요청 여부 (Authorization로 사용자 인증 사용 시 true)
            cors.setAllowCredentials(true);
            return cors;
        });
        // CSRF 기능을 비활성화한다.
        http.csrf().disable();
        http.headers().frameOptions().disable();
        http.authorizeRequests()
                // 누구나 h2-console 접속 허용
                .antMatchers("/h2-console/**").permitAll();

        // 기본 설정인 Session 방식은 사용하지 않고 JWT 방식을 사용하기 위한 설정
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //  요청에 대한 보안 검사를 구성한다.
        http.authorizeRequests()
                .antMatchers("/**").permitAll()
                .antMatchers("/kakao/**").permitAll()

                //  Spring Security 필터 체인을 구성하는 인터페이스다.
                .anyRequest().authenticated()
                // JWT 인증/인가를 사용하기 위한 설정
                // JwtAuthFilter를 UsernamePasswordAuthenticationFilter 이전에 실행되도록 설정한다. JwtAuthFilter는 JWT 토큰을 검증하고 인증/인가를 처리한다.
                .and().addFilterBefore(new JwtAuthFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);

        // 로그인 페이지를 설정한다
        // http.formLogin().loginPage("/api/user/login-page").permitAll();
        // http.exceptionHandling().accessDeniedPage("/api/user/forbidden");

        return http.build();

    }
}

이는 원본 코드의 리소스에 대한 요청을 보냈지만 서버에서 요청을 허용하지 않았기 때문입니다.

package com.sparta.petplace.config;

import com.sparta.petplace.auth.jwt.JwtAuthFilter;
import com.sparta.petplace.auth.jwt.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;

import java.util.Arrays;
import java.util.List;

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
@EnableGlobalMethodSecurity(securedEnabled = true) // @Secured 어노테이션 활성화
public class WebSecurityConfig {
    private final JwtUtil jwtUtil;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        // h2-console 사용 및 resources 접근 허용 설정
        return (web) -> web.ignoring()
                // .requestMatchers(PathRequest.toH2Console())
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }


    //  Spring Security 필터 체인을 구성하는 인터페이스다.
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // CORS 문제 해결
        http.cors().configurationSource(request -> {
            CorsConfiguration cors = new CorsConfiguration();
            //  모든 패턴 허용
            cors.setAllowedOriginPatterns(List.of("*"));
            //  API 메서드 허용 범위
            cors.setAllowedMethods(Arrays.asList("GET","POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
            //  test
            cors.setAllowedOrigins(List.of("https://fe-fawn.vercel.app"));
            //  Headers 모든 값
            cors.setAllowedHeaders(List.of("*"));
            //  header token "Authorization"값 허용
            cors.addExposedHeader("Authorization");
            //  header token "Refresh_Token"값 허용
            cors.addExposedHeader("RefreshToken");
            //  내 서버가 응답할 때 json을 JS에서 처리할 수 있게 하려면 설정 (허용하려면 true)
            //  사용자 자격증명과 함께 요청 여부 (Authorization로 사용자 인증 사용 시 true)
            cors.setAllowCredentials(true);
            return cors;
        });
        // CSRF 기능을 비활성화한다.
        http.csrf().disable();
        http.headers().frameOptions().disable();
        http.authorizeRequests()
                // 누구나 h2-console 접속 허용
                .antMatchers("/h2-console/**").permitAll();

        // 기본 설정인 Session 방식은 사용하지 않고 JWT 방식을 사용하기 위한 설정
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //  요청에 대한 보안 검사를 구성한다.
        http.authorizeRequests()
                .antMatchers("/**").permitAll()
                .antMatchers("/kakao/**").permitAll()

                //  Spring Security 필터 체인을 구성하는 인터페이스다.
                .anyRequest().authenticated()
                // JWT 인증/인가를 사용하기 위한 설정
                // JwtAuthFilter를 UsernamePasswordAuthenticationFilter 이전에 실행되도록 설정한다. JwtAuthFilter는 JWT 토큰을 검증하고 인증/인가를 처리한다.
                .and().addFilterBefore(new JwtAuthFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);

        // 로그인 페이지를 설정한다
        // http.formLogin().loginPage("/api/user/login-page").permitAll();
        // http.exceptionHandling().accessDeniedPage("/api/user/forbidden");

        return http.build();

    }
}

그래서 여기

cors.setAllowedOrigins(List.of("https://fe-fawn.vercel.app"));

이 부분을 추가하면 CORS 처리가 완료됩니다.

error: Content is protected !!