@@ -0,0 +1,81 @@
+package nextstep.security.filter;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import nextstep.security.authentication.Authentication;
+import nextstep.security.authentication.AuthenticationManager;
+import nextstep.security.authentication.UsernamePasswordAuthenticationToken;
+import nextstep.security.core.context.SecurityContext;
+import nextstep.security.core.context.SecurityContextHolder;
+import nextstep.security.exception.AuthenticationException;
+import nextstep.security.util.Base64Convertor;
+
+import java.io.IOException;
+
+public class BasicAuthFilter extends AbstractAuthProcessingFilter {
+    public static final String BASIC_HEADER_COLON = ":";
+    public static final String BASIC_HEADER_COMMA = " ";
+    public static final String BASIC_HEADER_PREFIX = "Basic ";
+    public static final String AUTHORIZATION = "Authorization";
+
+    public BasicAuthFilter(final AuthenticationManager authenticationManager) {
+        super(authenticationManager);
+    }
+
+    @Override
+    boolean match(final HttpServletRequest request) {
+        return existAuthorizationHeader(request);
+    }
+
+    private boolean existAuthorizationHeader(final HttpServletRequest request) {
+        return request.getHeader(AUTHORIZATION) != null;
+    }
+
+    @Override
+    public Authentication makeAuthentication(final HttpServletRequest request) {
+        String authorizationHeader = request.getHeader(AUTHORIZATION);
+
+        if (isValidBasicAuthHeader(authorizationHeader)) {
+            throw new AuthenticationException();
+        }
+
+        String credentials = extractCredentials(authorizationHeader);
+        String[] usernameAndPassword = parseCredentials(credentials);
+
+        return UsernamePasswordAuthenticationToken.unauthenticated(usernameAndPassword[0], usernameAndPassword[1]);
+    }
+
+    private boolean isValidBasicAuthHeader(final String authorizationHeader) {
+        return authorizationHeader == null || !authorizationHeader.startsWith(BASIC_HEADER_PREFIX);
+    }
+
+    private String extractCredentials(String authorizationHeader) {
+        String[] parts = authorizationHeader.split(BASIC_HEADER_COMMA);
+        if (parts.length != 2) {
+            throw new AuthenticationException();
+        }
+
+        return Base64Convertor.decode(parts[1]);
+    }
+
+    private String[] parseCredentials(String decodedString) {
+        String[] usernameAndPassword = decodedString.split(BASIC_HEADER_COLON);
+        if (usernameAndPassword.length != 2) {
+            throw new AuthenticationException();
+        }
+
+        return usernameAndPassword;
+    }
+
+    @Override
+    protected void successAuthentication(final HttpServletRequest request, final HttpServletResponse response,
+                                         final FilterChain filterChain) throws ServletException, IOException {
+        final SecurityContext context = SecurityContextHolder.getContext();
+        final Authentication authentication = context.getAuthentication();
+        authentication.getAuthorities().clear();
+
+        filterChain.doFilter(request, response);
+    }
+}

리뷰: ```suggestion
        authentication.getAuthorities().add(Role.ADMIN);

        filterChain.doFilter(request, response);
```

> SecurityContextRepository의 구현체를 Filter에서 주입받아 처리하게 구현해주셨네요!
> 스프링은 다른 Filter에서 SecurityContextRepository의 내부 구현을 몰라도 SecurityContextHolder.getContext()를 통해 인증 정보를 활용할 수 있도록 설계되어 있는데요.
> 이렇게 구조를 설계한 이유에 대해 고민해보시면 좋겠습니다!
>
>> 음.. 잘못짚은거 같네요..
>> security 코드를 보면 인증 후에 바로 저장을 하는데, filter 내부에서 각 SecurityContextRepository 구현체에 의존하고 있네요!

위 코멘트의 핵심은 `SecurityContextRepository를 Filter에서 모르는 상태로, SecurityContextHolder.getContext()를 활용한다.`인데요.
SecurityContextRepository로 관리하는 역할은 새로운 Filter를 만들어 역할을 전달하고, 이외 Filter는 SecurityContextRepository를 모르는 상태로 만들어보면 좋을 것 같아요!
추가 힌트가 필요하면 SecurityContextPersistenceFilter를 검색해보세요 :)