[ 정수원 스프링 시큐리티 #1 ] 스프링 시큐리티 기본 API & Filter 이해 (3)

김수호·2024년 3월 4일
0
post-thumbnail

지난 포스팅에 이어, 이번 포스팅에서는 6) ~ 8) 까지의 내용을 정리한다.

👉 목차는 다음과 같다.

1) 인증 API - 프로젝트 구성 및 의존성 추가
2) 인증 API - 사용자 정의 보안 기능 구현
3) 인증 API - Form Login 인증
4) 인증 API - Form Login 인증 필터 : UsernamePasswordAuthenticationFilter
5) 인증 API - Logout 처리, LogoutFilter
6) 인증 API - Remember Me 인증
7) 인증 API - Remember Me 인증 필터 : RememberMeAuthenticationFilter
8) 인증 API - 익명사용자 인증 필터 : AnonymousAuthenticationFilter

9) 인증 API - 동시 세션 제어, 세션 고정 보호, 세션 정책
10) 인증 API - 세션 제어 필터 : SessionManagementFilter, ConcurrentSessionFilter
11) 인가 API - 권한 설정 및 표현식
12) 인증/인가 API - 예외 처리 및 요청 캐시 필터 : ExceptionTranslationFilter, RequestCacheAwareFilter
13) Form 인증 - 사이트 간 요청 위조 : CSRF, CsrfFilter

바로 하나씩 확인해보자.


6) Remember Me 인증

  • 세션이 만료되고 웹 브라우저가 종료된 후에도 어플리케이션이 사용자를 기억하는 기능.
  • Remember-Me 쿠키에 대한 Http 요청을 확인한 후 토큰 기반 인증을 사용해 유효성을 검사하고 토큰이 검증되면 사용자는 로그인 된다.
  • 사용자 라이프 사이클
    • 인증 성공 (Remember-Me 쿠키 설정)
    • 인증 실패 (쿠키가 존재하면 쿠키 무효화)
    • 로그아웃 (쿠키가 존재하면 쿠키 무효화)
  • (참고) 설정 클래스에서 Remember Me 설정을 활성화 시키면 로그인 페이지에서 스프링 시큐리티가 Remember Me 인증 기능을 사용할 수 있도록 제공한다.
    • Remember Me 기능을 활성화한 상태에서 인증을 요청하게 되면, 인증 후 서버에서는 Remember Me 쿠키를 발급하여 응답 헤더에 실어서 보낸다.

 

스프링 시큐리티에서 Remember Me 인증 관련된 API를 어떻게 설정할 수 있는지 살펴보자.

  • 참고)
    • http.rememberMe() 을 작성함으로써 Remember me 기능이 작동한다.
    • 하위 API 에는 rememberMeParameter(..), tokenValiditySeconds(..) 등이 있다. ( 자세한 설명은 위 그림 참고 )
    • 참고) alwaysRemember(boolean) 은 일반적으로 false로 한다.
    • 참고) userDetailsService : remember me 기능을 수행할 때, 시스템에 있는 사용자 계정을 조회하는 처리 과정이 있는데, 그 처리를 위해서 필요한 클래스이다. ( remember me 인증시 userDetailsService(..) 는 꼭 필요한 설정이다. )

 

👉 코드로 작성해보자.

  • SecurityConfig 수정
  • 실행해보자.
    • 참고) 인가 정책에 의해 루트 경로(/) 접근시 로그인 페이지로 이동하는 것을 확인할 수 있다.
    • 참고) 화면을 보면 remember me 라는 기능이 추가된 것을 확인할 수 있다.
  • Remember me 체크박스를 체크하지 않고 그냥 로그인해보자.
    • 정상적으로 인증되어 루트 페이지로 이동했다.
    • 지금 이 상태를 설명하면 다음과 같다.
      • 우리가 인증이 되었다는 말은, 스프링 시큐리티에서 그 사용자의 세션이 생성되었고, 그리고 그 세션이, 성공한 인증 객체를 담고있는 상태를 말한다. 그리고 서버는 인증에 성공한 클라이언트에게, 세션을 생성할 때 가지고 있는 세션 아이디(JSESSIONID)를 응답 헤더에 실어서 보내고, 클라이언트는 해당 세션 아이디를 가지고 있게 된다. 따라서 이후 클라이언트의 요청부터는 그 세션 아이디가 서버로 요청되고, 서버에서는 클라이언트가 요청한 세션 아이디와 매칭되는 세션을 조회하고, 그 세션 내부에 SecurityContext에 있는 인증 객체를 조회해서, 이를 기준으로 스프링 시큐리티는 해당 사용자가 인증된 사용자인지 판단해서 처리하게 된다.
      • 따라서, 만약 클라이언트에서 JSESSIONID 쿠키 정보를 삭제하거나 쿠키 값을 변경하고 다시 새로고침 해보면, 이 사용자는 이미 이전에 인증을 받았는데도 불구하고, JSESSIONID가 유효하지 않은 상태로 서버에 요청되기 때문에, 서버에서는 해당 사용자가 마치 처음 접속한 사용자인 것 처럼 판단하고 처리한다. (따라서 사용자는 다시금 인증을 해야한다.)
  • 이번엔 remember-me를 체크하고 인증해보자.
    • 정상적으로 인증되어 루트 페이지로 이동했다.
    • 개발자 도구를 열어보면 JSESSIONID도 있지만, remember-me 라는 이름의 쿠키도 추가되었다.
      • 해당 쿠키 값에는 인코딩된 문자열이 나오는데, 이 문자열에는 인증 당시 사용했던 아이디, 패스워드, 만료일 등이 담겨져있다.
    • 이 상태에서 JSESSIONID 를 제거하고 새고고침 해보자. 이전에는 인증을 받았는데도 불구하고 다시 인증을 받아야했지만, 이번에는 인증을 받지 않아도 계속적으로 접근이 가능하다. 그 이유는, JSESSIONID가 없다고 하더라도 스프링 시큐리티에서는 remember-me 라는 쿠키를 가지고 왔을 경우를 체크하기 때문이다.
      • remember me 기능을 처리하는 필터인 rememberMeAuthenticationFilter에서 처리한다. (다음 내용에서 학습)
      • 즉, request 헤더에 remember-me 라는 쿠키명을 가진 값이 있다면, 이 값을 다시 복호화 및 파싱하고 추출해서, 아이디와 패스워드를 통해서 user 객체를 하나 얻고, 그 객체를 통해서 다시금 인증을 시도한다.

 

이번 내용에서는 Remember Me 기능에 대해서 알아보았다.
다음 내용에서는 Remember Me의 기능을 처리하는 필터인 RememberMeAuthenticationFilter 에 대해서 알아보자.


7) Remember Me 인증 필터 : RememberMeAuthenticationFilter

이번 내용에서는 RememberMeAuthenticationFilter 가 동작하는 처리 과정에 대해서 알아보자.

  • 참고)
    • 먼저, RememberMeAuthenticationFilter 가 사용자의 요청을 받아서 처리하는 조건이 있다.
      • 첫 번째 조건은 Authentication 인증 객체가 없어야(null) 한다. 만약 인증 객체가 있는 경우 라면, 이미 인증을 받은 것이기 때문에 다시금 인증을 진행할 필요가 없으므로 해당 필터가 동작하지 않는다.
      • 두 번째 조건은 사용자의 요청에 remember-me 쿠키 정보가 있어야 한다. 그래야 그 쿠키 정보로 인증 처리를 진행하기 때문이다.
      • 즉, 클라이언트의 세션은 무효화 되었지만, 요청 헤더에 remember-me 쿠키 정보를 가지고 있는 경우에 동작한다.
    • 이제 위 이미지 흐름을 확인해보자.
    • 1) 사용자가 요청을 시도한다.
      • 참고) 현재 사용자는 세션이 만료되었고, 폼 인증 당시 remember-me 기능을 활성화하여 인증을 받았다고 가정하자.
    • 2) RememberMeAuthenticationFilter 가 동작한다.
    • 3) RememberMeServices 인터페이스를 구현한 rememeber me 인증 처리를 하는 역할을 하는 2개의 구현체가 있다.
      • TokenBasedRememberMeServices : 메모리에 저장된 토큰과 사용자의 요청 쿠키 토큰을 비교해서 인증 처리한다. (기본적으로 토큰 만료 시간은 14일이다.)
      • PersistentTokenBasedRememberMeServices : 영구적인 방식으로, DB에 저장된 토큰과 사용자의 요청 쿠키 토큰을 비교해서 인증 처리한다.
    • 4) RememberMeServices 는 Token Cookie를 추출한다.
    • 5) 사용자가 가지고 있는 토큰(rememeber-me)의 존재여부에 따라, 존재하지 않으면 그 다음 필터로 이동한다.
    • 6) 토큰이 존재할 경우, 해당 토큰의 포맷이 정상적으로 규칙을 지키고 있는 토큰인지 판단한다. 만약 정상이 아닐 경우, 예외를 발생시킨다.
    • 7) 정상일 경우, 사용자의 토큰 값과 서버의 토큰 값이 일치하는지 확인한다. 일치하지 않는 경우 예외를 발생시킨다.
    • 8) 일치하는 경우, 해당 토큰에 포함된 정보 중 user 계정 정보가, DB에 존재하는지 확인한다. 존재하지 않는 경우, 예외를 발생시킨다.
    • 9) 존재하는 경우, 새로운 Authentication 인증 객체를 생성한다. (RememberMeAuthenticationToken)
    • 10) 그 인증 객체를 AuthenticationManager 에게 전달해서 인증 처리를 진행한다.

8) 익명사용자 인증 필터 : AnonymousAuthenticationFilter

이번 내용에서는 AnonymousAuthenticationFilter 에 대해서 알아보자.

  • 참고)
    • 먼저, AnonymousAuthenticationFilter 는 인증되지 않은 사용자에 대해서 익명사용자용 인증 객체를 생성해서 SecurityContext 객체 안에 저장한다.
    • 이제 위 이미지 흐름을 확인해보자.
    • 1) 사용자가 요청을 시도한다.
    • 2) AnonymousAuthenticationFilter 가 요청을 받는다.
    • 3) AnonymousAuthenticationFilter 는 현재 요청을 하는 사용자의 Authentication 인증 객체가 존재하는지 여부를 판단한다.
      • 이전 단계 필터에서 인증을 거친 사용자라면, SecurityContext 안에 Authentication 객체가 존재한다. Authentication 객체가 존재하지 않는다는 의미는, 해당 사용자는 인증을 받지 않은 사용자이다.
    • 4) 인증 객체가 존재하면, 해당 사용자는 인증을 받은 사용자이기 때문에, 해당 필터에서는 특별한 처리를 하지 않고, 그 다음 필터로 이동한다.
    • 5) 인증 객체가 존재하지 않는다면, 익명 사용자용 인증 객체인 AnonymousAuthenticationToken 을 생성한다. 그리고 SecurityContext 객체 안에 저장한다. 그리고 그 다음 필터로 이동한다.
      • 따라서, 이후 부터는 인증된 사용자인지 그렇지 않은 사용자인지 여부를 체크할 때, 인증 객체가 null 인지의 여부로 판단하는게 아니라, Authentication 인증 객체 타입을 통해서 Anonymous 인지 판단한다.
    • 참고) 익명사용자의 AnonymousAuthenticationToken 을 담은 SecurityContext 객체는 HttpSession 에는 저장되지 않는다. ( 인증 객체를 가지고 있긴 하지만, 실제로는 인증을 받지 않은 사용자이기 때문에, 인증 객체를 세션에 저장할 필요가 없다. )

 

✔️ 참고


강의를 듣고 정리한 글입니다. 코드와 그림 등의 출처는 정수원 강사님께 있습니다.

profile
현실에서 한 발자국

0개의 댓글