이 강의에서는
커스텀 필터를 만들어
이를 Spring Security FilterChain에
추가해 보겠습니다
addFilterBefore 메소드를 사용하여
Spring Security 프레임워크 내에서
사용 가능한 여러 커스텀 필터를
확인할 수 있습니다
첫 번째로 실행될 필터는
CorsFilter
또는 CsrfFilter 입니다
그 다음에는 또 다른 내장 필터인
BasicAuthenticationFilter가 있습니다
요구 사항이 인증 전에
일부 로직을 실행하는 것이라면
커스텀 필터를 만들고
이를 BasicAuthenticationFilter
바로 전에 구성해야 합니다
BasicAuthenticationFilter내에서
자격 증명을 추출하고
이를 기반으로
엔드 유저의 실제 인증이
발생할 것이기 때문입니다
따라서 제 목표는 인증 이전에
어떤 로직을 실행하는 것이므로
새로운 커스텀 필터인
RequestValidationFilter를 만들고
이를 BasicAuthenticationFilter 바로 전에
구성할 것입니다
여기서 주의할 점은
우리가 숫자 1,2,3 과 같은 숫자를 정의한 것은
이해를 돕기 위함이고
실제 Spring Security FilterChain 내에서는
숫자가
달라질 수 있습니다
이러한 숫자는
이해를 돕기 위한 것임을 이해하면 됩니다
RequestValidationFilter 내에서
작성할 로직은 만약 누군가가
'test' 값을 포함한
유저 이름으로 로그인을 시도한다면
유효하지 않은 요청이라는
오류를 엔드 유저에게 반환할 것입니다
그 말은 즉 'test'라는 유저 이름으로
로그인을 시도하는 경우에는
제 애플리케이션에 진입할 수 없게 됩니다
왜냐하면 실제 인증이 일어나지 않았고
그리고 제 커스텀 필터로는 모든 검색 요청을
거부할 것입니다
이제 이 커스텀 필터를
웹 애플리케이션에 구현해 보겠습니다
커스텀 필터를 만들기 위해
여기 프로젝트 내에서
filter 패키지로 이동합니다
기억하시면 이전에 CSRF 개념을 논의할 때
만든 패키지 입니다
그리고 이 패키지 내에 필터 하나가 있습니다
같은 패키지 내에
새로운 Java 클래스를 만들고
RequestValidationBeforeFilter라는 이름을
지정합니다
이 필터는 jakarta.servlet 패키지 내에
있는 필터 인터페이스를 구현하게 됩니다
이로써 doFilter 메소드를
오버라이트 해야한다는 에러가 뜹니다
그러면 doFilter 메소드를
실행해 봅시다
여기서 볼 수 있듯이 doFilter
ServletRequest, ServletResponse 그리고 FilterChain 세 가지 매개변수를 받는
비어있는 실행이 이루어짐을 알 수 있습니다
따라서 여기서는 이 필터의 일부분으로 실행하고 싶은 로직을 넣어야합니다
이를 위해 몇 줄의 코드를 넣어
자세히 살펴보겠습니다
여기서 먼저 저는 두 가지 클래스 레벨 필드를 만들었습니다
doFilter 메소드 내에
몇 줄의 코드를 넣겠습니다
이제 이 로직이 무엇인지 살펴봅시다
여기서 볼 수 있듯이
request와 response를
HttpServletRequest 및 HttpServletResponse로 형변환하려고 합니다
request 다음에
Angular UI 애플리케이션을 위한
Authorization header를 얻고자 합니다
이 Authorization header를 확보하면
header값 내에 스페이스가 없도록
조정할 것입니다
이 Angular 애플리케이션을 보면
우리가 가지고 있는 인터셉터 클래스로 들어가면
우리는 Basic이라는 값 뒤에
스페이스가 있고
Authorization header를 보냅니다
다음으로 우리는 이메일과 비밀번호의
base 64 값을 콜론으로 구분하여 보내려고 시도하고 있습니다
이 header를 읽은 다음에 하는 일은
header 내의 여섯 번째 부분에서
문자열을 추출하는 것입니다
왜냐하면 우리는
Authorization header 내에 있는 초기 텍스트
즉 basic을 사용하고 싶지 않기 때문입니다
우리는 basic과 스페이스를 포함한
첫 6글자를 무시하고
6번째 글자부터 substring 기능을
시작합니다
즉 우리는 header의 7번째
글자부터 읽기 시작합니다
왜냐하면 인덱스는 항상 0부터 시작하기 때문입니다
따라서 이 값이 base64Token이 될 것이고
이를 디코딩합니다
디코딩하면
정희한 구분자인 콜론을 사용하여
문자열을 분리하려고 시도하는 것입니다
구분자의 값인 콜론을 활용하여
우리는 substring을 사용하고 Authentication header 내에서
가장 첫 두 값을 추출하려고 시도합니다
이것은 username 입니다
유저 이름 또는 이메일을 확인하는 데 더 관심이 있으므로
이메일을 추출하고
나중에 이메일이 "test"라는
값이 있는지 확인합니다
만약 그렇다면 저는
BAD_REQUEST = 400 에러 상태를 반환할 것입니다
그렇지 않고 요청 내의 문제가 없다면
단순히 이 체인 내의 다음 필터를
이 체인 객체의 도움을 통해 호출할 것입니다
따라서 이 체인 객체는 doFilter 메소드에
세 번째 매개변수가 됩니다
이것은 간단한 내용으로 잘 이해가 되었길 바랍니다
GitHub 내에서 코드를 보고
이해할 수도 있습니다
이때 우리는 그냥 커스텀 필터를 보호하면 됩니다
다음 스텝으로는 우리는 basicAuthenticationFilter 직전에
커스텀 필터를 주입합니다
이를 위해 우리는 ProjectSecurityCofig 클래스로
이동합니다
이 클래스에서
이제 우리는 자체적인 커스텀 필터를
BasicAuthenticationFilter 앞에서 실행되도록 구성할 수 있습니다
여기 볼 수 있듯이
우리는 이미 하나의 커스텀 필터를 구성했고
그것은 CsrfCookieFilter 입니다
비슷하게 우리는 고유의 커스텀 필터를
구성할 수도 있습니다
이를 위해 먼저
addFilterBefore 메소드를 호출합니다
그리고 이 메소드에는 우리의 커스텀 필터 객체를
전달해야 합니다
이것은 RequestValidationBeforeFilter 입니다
그런 다음 두 번째 메소드 매개변수로
여기서 고려해야 할
Spring Security 프레임워크의 필터 이름을
전달해야 합니다
따라서 여기서는 BasicAuthenticationFIlter를 고려합니다
이로써 필요한 모든 변경사항을 마무리했습니다
이제 주석 처리된 코드를 모두 정리해서
깔끔하게 만들겠습니다
변경사항을 저장하고 빌드를 합니다
빌드가 끝나면 우리는 UI 애플리케이션으로 가서
로그인 플로우를 테스트 합니다
그 전에 콘솔을 먼저 정리합니다
여기서 로그인을 happy@example.com과
비밀번호 12345를
활용하여
로그인을 시도합니다
로그인이 성공적으로 이루어졌습니다
콘솔로 돌아가서 확인하면
Spring Security 프레임워크에서 실행된
Security filter chain을 확인할 수 있습니다
BasicAuthenticationFilter 직전에
커스텀 필터도 실행되었습니다
근데 우리의 이메일이 괜찮고
그리고 test로 시작하는 값도 없어기에
아무런 에러가 없었습니다
따라서 우리는 test 라는 값을 포함한
유저 이름으로 시도해볼 수 있습니다
로그아웃 해봅시다
이제 happytest@example.com를 이용하여 로그인하고
개발자 콘솔을 여기서 열어보겠습니다
로그인을 누르면
커스텀 필터 내의 실행이 멈췄습니다
한 줄씩 디버깅 해보면
이것이 제가 입력한 이메일임을 볼 수 있습니다
이것이 test 를 포함한 값이기 때문에
if 블록 내로 들어가고
단순히 에러 400이 뜨게 됩니다
그리고 콘솔 내에서
BAD REQUEST 의미하는 에러 400을 볼 수 있습니다
물론 프로덕션 애플리케이션에서는
이 에러 400을 인지하고
엔드 유저에게 UI 애플리케이션 내에서 보여주는 것입니다
그렇지만 지금은 개발자 콘솔에서
다음과 같은 에러를
보는 것이 괜찮습니다
인증 전에
실행하고 싶은 커스텀 로직이 있다면
커스텀 필터를 작성하고
addFilterBefore 메소드의 도움을 통해 구성하면 됩니다
그러나 어느 커스텀 필터이든
시간이 오래 걸리는 로직은
사용하지 않는 것이 좋습니다
예를 들어 DB 쿼리와 몇 천개의 코드들은
애플리케이션의 몇 가지 실행에
영향을 미치기 때문입니다
그 이유는 웹 애플리케이션으로 오는 각각의 요청(request)이
커스텀 필터로 인해 걸러지고
다른 요청들에도 똑같이
실행되기 때문입니다
따라서 비즈니스 또는 클라이언트에서
필수로 지정한 로직만 작성하도록
꼭 신중하게 고려해야 합니다
내용이 잘 이해가 되었길 바랍니다
감사합니다 다음 강의에서 뵙겠습니다 수고하셨습니다