@@ -0,0 +1,86 @@
+package nextstep.app;
+
+import nextstep.app.domain.CustomUserDetailsService;
+import nextstep.app.domain.MemberRepository;
+import nextstep.security.role.Role;
+import nextstep.security.core.context.HttpSessionSecurityContextRepository;
+import nextstep.security.core.context.SecurityContextRepository;
+import nextstep.security.filter.AuthorizationFilter;
+import nextstep.security.filter.GlobalExceptionFilter;
+import nextstep.security.filter.SecurityContextHolderFilter;
+import nextstep.security.filter.config.DefaultSecurityFilterChain;
+import nextstep.security.filter.config.DelegatingFilterProxy;
+import nextstep.security.filter.config.FilterChainProxy;
+import nextstep.security.filter.config.SecurityFilterChain;
+import nextstep.security.core.uesrdetails.UserDetailsService;
+import nextstep.security.authentication.AuthenticationManager;
+import nextstep.security.authentication.AuthenticationProvider;
+import nextstep.security.authentication.DaoAuthenticationProvider;
+import nextstep.security.authentication.ProviderManager;
+import nextstep.security.filter.BasicAuthFilter;
+import nextstep.security.filter.FormAuthFilter;
+import nextstep.security.util.PasswordMatcher;
+import nextstep.security.util.PlainTextPasswordMatcher;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+import java.util.Map;
+
+@Configuration
+public class SecurityConfig {
+    private final MemberRepository memberRepository;
+
+    public SecurityConfig(final MemberRepository memberRepository) {
+        this.memberRepository = memberRepository;
+    }
+
+    @Bean
+    public DelegatingFilterProxy delegatingFilterProxy() {
+        return new DelegatingFilterProxy(filterChainProxy(List.of(securityFilterChain())));
+    }
+
+    @Bean
+    public FilterChainProxy filterChainProxy(List<SecurityFilterChain> securityFilterChains) {
+        return new FilterChainProxy(securityFilterChains);
+    }
+
+    @Bean
+    public SecurityFilterChain securityFilterChain() {
+        return new DefaultSecurityFilterChain(
+                new GlobalExceptionFilter(),
+                new SecurityContextHolderFilter(securityContextRepository()),
+                new FormAuthFilter(authenticationManager()),
+                new BasicAuthFilter(authenticationManager()),
+                new AuthorizationFilter(Map.of("/members", List.of(new Role("NORMAL")))
+            )
+        );
+    }
+
+    @Bean
+    public SecurityContextRepository securityContextRepository() {
+        return new HttpSessionSecurityContextRepository();
+    }
+
+    @Bean
+    public AuthenticationManager authenticationManager() {
+        List<AuthenticationProvider> providers = List.of(daoAuthenticationProvider());
+        return new ProviderManager(providers);
+    }
+
+    @Bean
+    public DaoAuthenticationProvider daoAuthenticationProvider() {
+        DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordMatcher(), userDetailsService());
+        return provider;
+    }
+
+    @Bean
+    public PasswordMatcher passwordMatcher() {
+        return new PlainTextPasswordMatcher();
+    }
+
+    @Bean
+    public UserDetailsService userDetailsService() {
+        return new CustomUserDetailsService(memberRepository);
+    }
+}

리뷰: > DefaultSecurityFilterChain의 match 기준이 뭘까요?

여러 필터 체인이 존재할 수 있고, 필터 체인마다 조건을 설정할 수 있습니다. 예를 들어 AuthorizationFilter의 "/members"가 일치하는지의 조건도 DefaultSecurityFilterChain의 match로 옮기는 것도 하나의 예시가 될 수 있을 것 같은데요.
이번 미션에서 중요한 포인트는 아니기도 하고 지금 구현하는 규모에선 Filter에서 검증하는 것과 차이를 느끼지 힘들 수 있을 것 같아서 4주 차 리팩터링 때 자세히 구현해보면 좋을 것 같습니다 ㅎㅎ 

추가로 사소한 팁인데 메서드 매개변수가 하나고 List를 받을 때 가변인자로 만들면 생각보다 편해서 좋습니다!