Spring Security

정민주·2024년 2월 12일

스프링 스터디

목록 보기
2/17
post-thumbnail

스프링 기반의 애플리케이션의 보안과 인증을 담당하는 프레임워크
-> 스프링시큐리티는 "filter"를 기반으로 동작하기에 스프링 MVC와 별도로 동작한다.

☑️단어체크

  • 접근 주체(Principal)
    : 이는 보안 시스템에서 현재 사용자를 나타냅니다. 쉽게 말해 현재 애플리케이션을 사용 중인 사람, 또는 시스템을 사용하는 누군가를 의미합니다. 예를 들어, 웹사이트에 로그인한 사용자, 앱을 사용하는 사람, 서버에 접속한 클라이언트 등이 모두 접근 주체가 될 수 있습니다.
  • 인증(Authentication)
    : 인증은 '증명하다'라는 의미로 예를 들어, 유저 아이디와 비밀번호를 이용하여 로그인 하는 과정 을 말합니다.
  • 인가(Authorization)
    : '권한부여'나 '허가'와 같은 의미로 사용됩니다. 즉, 어떤 대상이 특정 목적을 실현하도록 허용(Access) 하는 것을 의미합니다.
  • 권한
    : 인증된 주체가 애플리케이션의 동작을 수행할 수 있도록 허락되었는지를 결정할 때 사용합니다.

🤫Spring Security_1

[참고]

Filter는 Dispathcer Servlet으로 가기 전에 먼저 클라이언트의 URL 요청을 받는다.
(참고로 InterCeptor는 Controller와 디스패처 서블릿 사이에 존재한다. 즉 둘은 적용 시기가 다르다.)

SpringSecuriet 3.2부턴은 xml로 설정하지 않고 자바 config 설정으로 간단하게 해결할 수 있다.


🤓Spring Security Filter

DispatcherServlet이 요청 받기 전에 다양한 필터들이 있을 수 있습니다.

필터가 하는 역할은 클라이언트와 자원 사이에서 요청과 응답 정보를 이용해 다양한 처리를 하는데 목적이 있습니다.

예를 들어 어떤 필터는 요청을 받은 후, 클라이언트가 원래 요청한 자원이 아닌 다른 자원으로 리다이렉트 시킬 수도 있습니다. 어떤 필터는 다음 필터에게 요청과 응답을 전달하지 않고, 바로 클라이언트에게 응답하고 끝낼 수도 있습니다.

스프링 시큐리티는 다양한 기능을 가진 필터들을 10개 이상 기본적으로 제공합니다. 이렇게 제공되는 필터들을 Security Filter Chain(시큐리티 필터 체인)이라고 말합니다.


⛓️Security Filter Chain


위의 그림은 시큐리티 필터 체인과 각각의 필터에서 사용하는 객체들(Repository, Handler, Manager등)에 대해 잘 표현하고 있습니다.

먼저 스프링 시큐리티가 제공하는 필터들이 어떤 역할을 담당하는지 정리해보도록 하겠습니다.

  • SecurityContextPersistenceFilter
    : SecurityContextRepository에서 SecurityContext를 가져오거나 저장하는 역할을 한다.
  • LogoutFilter
    : 설정된 로그아웃 URL로 오는 요청을 감시하며, 해당 유저를 로그아웃 처리
  • (UsernamePassword)AuthenticationFilter
    : (아이디와 비밀번호를 사용하는 form 기반 인증) 설정된 로그인 URL로 오는 요청을 감시하며, 유저 인증 처리
    1. AuthenticationManager를 통한 인증 실행
    2. 인증 성공 시, 얻은 Authentication 객체를 SecurityContext에 저장 후
      AuthenticationSuccessHandler 실행
    3. 인증 실패 시, AuthenticationFailureHandler 실행
  • DefaultLoginPageGeneratingFilter
    : 인증을 위한 로그인폼 URL을 감시한다.
  • BasicAuthenticationFilter
    : HTTP 기본 인증 헤더를 감시하여 처리한다.
  • RequestCacheAwareFilter
    : 로그인 성공 후, 원래 요청 정보를 재구성하기 위해 사용된다.
  • SecurityContextHolderAwareRequestFilter
    : HttpServletRequestWrapper를 상속한 SecurityContextHolderAware RequestWapper 클래스로 HttpServletRequest 정보를 감싼다. Security ContextHolderAwareRequestWrapper 클래스는 필터 체인상의 다음 필터들에게 부가정보를 제공한다.
  • AnonymousAuthenticationFilter
    : 이 필터가 호출되는 시점까지 사용자 정보가 인증되지 않았다면 인증토큰에 사용자가 익명 사용자로 나타난다.
  • SessionManagementFilter
    : 이 필터는 인증된 사용자와 관련된 모든 세션을 추적한다.
  • ExceptionTranslationFilter
    : 이 필터는 보호된 요청을 처리하는 중에 발생할 수 있는 예외를 위임하거나 전달하는 역할을 한다.
  • FilterSecurityInterceptor
    : 이 필터는 AccessDecisionManager 로 권한부여 처리를 위임함으로써 접근 제어 결정을 쉽게해준다.

🤯Spring Security_2

해당 포스트의 메인이 되는 내용이다.

🔥🔥차근차근 설명해보겠다! 일단 전체적인 과정은 다음과 같다.

  1. 사용자가 로그인 정보와 함께 인증 요청(Http Request)

  2. AuthenticationFilter가 이 요청을 가로챈다. 이때 가로챈 정보를 통해 UsernamePasswordAuthenticationToken이라는 인증용 객체를 생성한다.

  3. 생성된 객체를 AuthenticationManager의 구현체인 ProviderManager에게 전달한다.

  4. 해당 UsernamePasswordAuthenticationToken 객체는 AuthenticationProvider에게 전달된다.
    : 여기서 해당 객체와 알맞은 인증방법을 찾는다.

  5. 적절한 AuthenticationProvider가 선택되면, 사용자 인증정보를 가져오는 UserDetailsService에 사용자 정보(UsernamePasswordAuthenticationToken)를 넘겨준다.

  6. UserDetailsService는 사용자 이름을 이용하여 데이터베이스에서 사용자 정보를 조회하고, 이 정보를 바탕으로 UserDetails 객체를 생성한다.
    : 해당 객체에 사용자의 인증 정보 역시 포함하고 있다.

  7. AuthenticationProvider는 UserDetails를 넘겨받고 사용자 정보를 비교한다.

  8. 인증이 완료되면 권한 등의 사용자 정보를 담은 Authentication 객체를 반환한다.

  9. 다시 최초의 AuthenticationFilter에 Authentication 객체가 반환된다.

  10. Authentication 객체를 SecurityContext에 저장한다


1. 사용자가 로그인 정보와 함께 인증 요청(Http Request)

말 그대로 사용자가 로그인 요청을 한다는 뜻이다.이번 프로젝트에서는 JSON 형태로 데이터를 주고받을 것이다.


2. AuthenticationFilter가 이 요청을 가로챈다.

: 해당 필터는 UsernamePasswordAuthenticationToken이라는 인증용 객체를 생성한다. ⭐해당 토큰은 사용자의 아이디와 비밀번호를 담은 인증 토큰 객체이다.⭐


3. 생성된 객체를 AuthenticationManager의 구현체인 ProviderManager에게 전달한다.

기본적으로 AuthenticationManager는 사용자가 제공한 인증 정보(Authentication 객체)를 받아 인증 작업을 수행하고, 인증된 정보를 다시 Authentication 객체에 담아 반환하는 역할을 한다.

해당 인터페이스의 대표적 구현체는 ProviderManager로써, AuthenticationProvider를 관리하며, 각 AuthenticationProvider를 통해 실제 인증 작업을 수행한다.


4. 해당 UsernamePasswordAuthenticationToken 객체는 AuthenticationProvider에게 전달된다.

: 여기서 해당 객체와 알맞은 인증방법을 찾는다.

AuthenticationProvider는 다양한 인증방식을 지원하는 Spring의 특성 상, 다양한 인증방법을 보유한다.

예를 들어, 사용자 이름과 비밀번호를 사용한 폼 기반 인증을 위한 DaoAuthenticationProvider, LDAP 인증을 위한 LdapAuthenticationProvider, OAuth2 인증을 위한 OAuth2AuthenticationProvider 등이 있다.


5. 적절한 AuthenticationProvider가 선택되면, 사용자 인증정보를 가져오는 UserDetailsService에 사용자 정보(UsernamePasswordAuthenticationToken)를 넘겨준다.

주어진 Authentication 객체가 어떤 인증 방식과 일치하는지를 판단하고, 그에 맞는 인증 과정을 수행한다. 각 AuthenticationProvider는 supports 메소드를 통해 자신이 처리할 수 있는 Authentication 타입을 알려주므로, ProviderManager는 이 정보를 바탕으로 적절한 AuthenticationProvider를 선택한다.

일단 적절한 AuthenticationProvider가 선택되면, 이 AuthenticationProvider는 UserDetailsService를 이용해서 사용자 정보를 조회하고, 이 정보를 바탕으로 인증을 수행한다. (인증 수행은 현재 단계의 일은 아니다. 여기선 그냥 알맞은 인증 방식 선택!)


6. UserDetailsService는 사용자 이름을 이용하여 데이터베이스에서 사용자 정보를 조회하고, 이 정보를 바탕으로 UserDetails 객체를 생성한다.

UserDetailsService는 사용자 이름을 이용하여 데이터베이스에서 사용자 정보를 조회하고, 이 정보를 바탕으로 UserDetails 객체를 생성한다. 이렇게 생성된 UserDetails 객체는 사용자의 인증 정보를 포함하고 있다.


7. AuthenticationProvider는 UserDetails를 넘겨받고 사용자 정보를 비교한다.

이전에 받은 인증객체인 UsernamePasswordAuthenticationToken와 DB의 데이터를 조회해 받아온 객체 UserDetails를 비교한다.

주요하게 비교하는 것이 바로 각 객체들에 저장된 비밀번호가 같은지이다!

만약 비밀번호가 일치하면 인증이 성공한 것으로 간주하고, 인증 성공 정보를 포함한 새로운 Authentication 객체를 생성하여 반환한다.


8. 인증이 완료되면 권한 등의 사용자 정보를 담은 Authentication 객체를 반환한다.


9. 다시 최초의 AuthenticationFilter에 Authentication 객체가 반환된다.


10. Authentication 객체를 SecurityContext에 저장한다


스프링 시큐리티와 JWT(Json Web Tokens)를 활용한 인증 및 인가 과정은 대략적으로 이해하신 것과 유사하나, 몇 가지 중요한 세부사항을 명확히 하는 것이 필요합니다. 여기서 설명드리는 과정은 일반적인 스프링 부트와 스프링 시큐리티를 사용한 JWT 인증 및 인가의 흐름입니다.

인증 과정 (Authentication)

사용자가 로그인을 요청하면, UsernamePasswordAuthenticationFilter가 이 요청을 가로챕니다.
사용자의 아이디와 비밀번호를 받아 AuthenticationManager를 통해 사용자의 인증 정보(UsernamePasswordAuthenticationToken)를 생성합니다.
인증이 성공하면, 인증 정보를 바탕으로 JWT 토큰을 생성합니다. 이 토큰에는 사용자의 식별 정보, 권한 정보 등이 포함될 수 있습니다.
생성된 JWT 토큰은 클라이언트에게 반환됩니다.
인가 과정 (Authorization)

클라이언트는 서버로 요청을 보낼 때 HTTP 헤더에 JWT 토큰을 포함시켜 전송합니다.
서버 측에서는 OncePerRequestFilter를 상속받은 커스텀 필터(예: JwtTokenFilter)에서 이 토큰을 가로챕니다.
해당 필터 내에서 JWT 토큰의 유효성을 검증합니다. 이 과정에서 JWT 서명을 검증하고, 토큰의 만료 여부 등을 확인합니다.
토큰이 유효하다면, 토큰에 포함된 사용자 식별 정보를 바탕으로 Authentication 객체를 생성하고, SecurityContextHolder에 이를 설정합니다. 이를 통해 스프링 시큐리티 컨텍스트에 인증 정보가 등록됩니다.
이후, 스프링 시큐리티는 SecurityContextHolder에 설정된 인증 정보를 바탕으로 요청에 대한 접근 권한을 결정합니다.
즉, 클라이언트로부터 받은 JWT 토큰이 특정 사용자의 것인지는 토큰 안에 포함된 식별 정보(예: 사용자 ID, 사용자 이름 등)를 통해 알 수 있습니다. JWT 토큰은 서명되어 있어, 서버가 토큰의 유효성을 검증할 수 있으며, 이를 통해 토큰에 포함된 정보의 신뢰성을 확보할 수 있습니다.

따라서, 스프링 애플리케이션에서는 클라이언트로부터 전송받은 JWT 토큰을 해석하고 검증하는 과정을 통해 해당 요청이 특정 사용자로부터 온 것임을 식별하고, 해당 사용자에게 적절한 권한을 부여하여 요청을 처리하게 됩니다.

++++

참고

0개의 댓글