Spring Security - Form 기반 인증

생각할 거리

Spring-Session과 Spring Security는 특별한 바인딩이 필요 없이 개별 Config만 설정해주면 되는 것 같다.

아무래도 Spring Session이 HttpSession 관련 구현체(?)를 구성하고 Spring Security는 구성된 HttpSession을 사용하는 듯 하다.

즉, Spring Security에서는 딱히 Spring Session에 대한 정보가 필요 없이 추상화에 의존하기 때문에 협력이 가능한 듯 보인다.

다만 인증 및 인가 관련되서 Session 처리는 Spring Security의 구성으로 진행해야 하는 듯 보인다. 아직까지는.

https://www.baeldung.com/spring-session

생각해보니 Spring Session도 Filter Chain 영역에서 프로세스가 진행되고 Spring Security 역시 Filter Chain에 물려서 프로세스가 진행된다.

그리고 이를 가능하게 해주는게 DelegatingFilterProxy 인데 Bean Filter에 위임을 해준다.

그럼 springSecurityFilterProxy와 Spring Session 관련 Bean Filter도 여기서 동작을?!

Spring Session 관련해서 정리해놨던 글을 좀 찾아봐야 겠다.


UserDetailsService, UserDetails 객체

각 객체들의 역할과 책임이 뭘까.

시큐리티 설정 파일(SecurityConfig)에서 인증 관련 설정(AuthenticationManager)하는 부분은

UserDetailsService다.

이 UserDetailsService가 사용하는 정보가 UserDetails

그리고 이 UserDetails를 기본 구현한 것이 User

이를 상속하여 구현한 것이 AccountContext

그리고 이 객체의 협력 중 가장 상위 개념인 UserDetailsService가 AutenticationManager에 설정이 된다.

AuthenticationManager는 Security Filter에서 사용이 된다.

이 흐름을 기억하자.

그런데 이 녀석들은 Form Login 방식에서만 유효하지 않을까?

그러니까 Form Login 방식에서 사용하는 Filter의 문맥에서만 유효하지 않을까 싶다.

그건 차차 알아가보도록 하자.

유저의 인증에 필요한 세부 ‘정보’는 UserDetailsService고 이는 AuthenticationManager에 담긴다.

이 세부 ‘정보’에는 username(id), password, GrantedAuthority가 있겠다.

이 정보는 UserDetails에 담긴다.(일종의 컨텍스트)

UserDetailsServices는 일종의 퍼사드 역할이라고 보면 될 듯 하다.


키워드

  • UserDetailsService <>
    • CustomUserDetailsService<>

⇒ 유저의 정보를 로드

프레임워크 전반에 걸쳐서 DAO로써 활용되거나 DaoAuthenticationProvider가 사용한다.

  • UserDetails <>
    • User <>
      • AccountContext<>


AuthenticationProvider 객체 활용 및 구현

위에서 구현 및 활용한 UserDetailsService가 반환하는 UserDetails에 대해서 검증을 진행해야 함

이러한 검증은 AuthenticationProvider중 하나가 사용되는데, 우리는 이를 구현한 CustomAuthenticationProvider를 활용할 예정


CustomAuthenticationProvider

AuthenticationManager(ProviderManager)가 호출할 CustomAuthenticationProvider를 구현하여 인증 유저에 대한 검증 로직을 수행


Logout 및 보안처리

  • SecurityContextLogoutHandler를 사용할 수 있다.

이쯤에서 Spring Session과 함께 해보자.

https://www.baeldung.com/spring-session-jdbc

https://www.javadevjournal.com/spring/spring-session-with-jdbc/

  • spring-session-jdbc

디펜던시를 확인해보면 자동으로 session 관련 sql을 실행할 수 있게끔 sql 파일을 갖고 있음.

Configuration에서 @EnableJdbcHttpSession을 해주면 안된다. → Spring Autoconfiguration이 작동하지 않음(직접 작성하는것으로 인식)

따라서 @SpringBootApplication과 함께 위 어노테이션을 붙여주자.

뭔가 postgresql은 sqlsession table을 직접 생성해줘야 하나보다..

그리고 Account 객체를 Serializable 구현을 해줘야한다. → 세션에 이 객체가 직렬화 되어 저장되나 보다.



인증 추가 정보 전달 및 설정

WebAuthenticationDetails, AuthenticationDetailsSource

  • AuthenticationFilter(UsernamePasswordAuthenticationFilter)
    • Security Filter 중 인증에 대한 책임을 담당하는 필터
  • WebAuthenticationDetails
    • 인증 과정 중 전달된 데이터를 저장
    • Authentication의 details 속성에 저장
  • AuthenticationDetailsSource
    • WebAuthenticationDetails 객체를 생성

클라이언트가 인증 정보(username, password) 외의 데이터를 전달하는 경우가 있다.

그리고 이 데이터들은 인증 과정, 인증 이후 등에서 필요할 때 사용이 되어야 할 수 있다.

이 때 이 정보들을 Authentication 객체의 details 속성에 담아서 저장을 하여 사용하는데, 이 details 객체의 타입은 object로써 어떤 타입도 저장이 가능하다.

그리고 그 details 중 하나가 바로 WebAuthenticationDetails이다.

WebAuthenticationDetails 객체를 생성하는 객체가 AuthenticationDetailsSource

즉, WebAuthenticationDetails 객체는 클라이언트가 추가적으로 전달하는 데이터를 저장한다.

스프링 시큐리티는 기본적으로 remoteAddress, SessionId는 제공한다.

간단하게 AuthenticationFilter의 구현체 중 하나인 UsernamePasswordAuthenticationFilter의 코드를 살펴보면

추가 설명이 필요 없이 위 코드의 주석을 확인해보면

  • 클라이언트의 요청
  • AuthenticationFilter(UsernamePasswordAuthenticationFilter)가 호출됨
    • 클라이언트가 전달한 request로부터 username, password를 얻어옴
    • 인증을 위한(아직 인증은 안된) Authentication(UsernamePasswordAuthenticationToken) 생성
    • 생성한 Authentication에 details 속성 설정(setDetails)
  • AuthenticationDetailsSource가 WebAuthenticationDetails를 세팅

detailsAuthentication 객체가 활용될 수 있는 곳이라면 어디든 전역적으로 사용 할 수 있다.


인증 성공 핸들러

인증 성공 핸들러에서 세션에 필요한 요소들을 추가하는 등 처리할 수 있을듯.


CustomAuthenticationSuccessHandler

인증 성공 이후 AuthenticationSuccessHanlder를 호출하여 후속 작업을 할 수 있다.

AuthenticationSuccessHadnler는 인터페이스라서 아래 두 개 작업이 가능하다.

  • 직접 구현체를 만들어서 등록한다.
  • 스프링 시큐리티가 기본적으로 제공하는 클래스를 상속하여 추가 구현한다.

강좌에서는 상속을 이용해서 진행



인증 실패 핸들러 구현

인증 실패 시 AuthenticationFilter는 AuthenticationFailureHandler를 호출한다.

그리고 AuthenticationFailureHandler는 인터페이스로, 사용하는 방법에는 두 가지가 있다.

  • 직접 구현체 구현 및 등록
  • 스프링 시큐리티가 기본적으로 제공하는 구현체 상속

이번 강좌에서는 기본적으로 제공하는 구현체를 상속하는 방법으로 진행한다.


CustomAuthenticationFailureHandler

인증 프로세스 중 발생한 예외에 대해서 각각 에러 메시지를 설정했다.

그리고 인증 실패에 따른 이동 경로는 /login?error=true&exception = errorMessage 처럼 처리를 하고 이후 작업에 대해서 부모 클래스에 위임을 해준다.

이렇게 이동 경로로 redirect하게 되면 login Controller가 위 요청을 받아 처리한다.


인증 거부(인가 안됨) 처리

인증은 성공했지만 성공 이후 클라이언트가 접근하려는 자원에 대한 권한이 없을 경우 인가 예외(AcessDeniedException)가 발생하게 된다.

인가 예외인증 필터가 처리하는게 아니라 ExceptionTranslationFilter가 처리한다.

즉, 유저에 대한 인증은 처리되었지만 인증과 동시에 접근하려는 자원에 대한 인가 예외(AccessDeniedException)가 발생한 경우 ExceptionTranslationFilter에서 AccessDeniedHandler를 호출하여 처리한다.

우리는 여기서 AccessDeniedHandler를 구현해보자.


CustomAccessDeniedHandler