yeom yaloo·2024년 1월 9일
0

스프링 시큐리티

[구조]

세부 구조

1. AuthenticationFilter

1-0. Security의 Filter의 종류

  • HeaderWriterFilter : Request의 Http 해더를 검사하여 header를 추가하거나 빼주는 역할을 한다.
  • CorsFilter : 허가된 사이트나 클라이언트의 요청인지 검사하는 역할을 한다.
  • CsrfFilter : Post나 Put과 같이 리소스를 변경하는 요청의 경우 내가 내보냈던 리소스에서 올라온 요청인지 확인한다.
  • LogoutFilter : Request가 로그아웃하겠다고 하는것인지 체크한다.
  • UsernamePasswordAuthenticationFilter : username / password 로 로그인을 하려고 하는지 체크하여 승인이 되면 Authentication을 부여하고 이동 할 페이지로 이동한다.
  • ConcurrentSessionFilter : 동시 접속을 허용할지 체크한다.
  • BearerTokenAuthenticationFilter : Authorization 해더에 Bearer 토큰을 인증해주는 역할을 한다.
  • BasicAuthenticationFilter : Authorization 해더에 Basic 토큰을 인증해주는 역할을 한다.
  • RequestCacheAwareFilter : request한 내용을 다음에 필요할 수 있어서 Cache에 담아주는 역할을 한다. 다음 Request가 오면 이전의 Cache값을 줄 수 있다.
  • SecurityContextHolderAwareRequestFilter : 보안 관련 Servlet 3 스펙을 지원하기 위한 필터라고 한다.
  • RememberMeAuthenticationFilter : 아직 Authentication 인증이 안된 경우라면 RememberMe 쿠키를 검사해서 인증 처리해준다.
  • AnonymousAuthenticationFilter : 앞선 필터를 통해 인증이 아직도 안되었으면 해당 유저는 익명 사용자라고 Authentication을 정해주는 역할을 한다. (Authentication이 Null인 것을 방지!!)
  • SessionManagementFilter : 서버에서 지정한 세션정책에 맞게 사용자가 사용하고 있는지 검사하는 역할을 한다.
  • ExcpetionTranslationFilter : 해당 필터 이후에 인증이나 권한 예외가 발생하면 해당 필터가 처리를 해준다.
  • FilterSecurityInterceptor : 사용자가 요청한 request에 들어가고 결과를 리턴해도 되는 권한(Authorization)이 있는지를 체크한다. 해당 필터에서 권한이 없다는 결과가 나온다면 위의 ExcpetionTranslationFilter필터에서 Exception을 처리해준다.

1-1. 서블릿 필터

  • 필터는 스프링과 무관하게 존재하고 서블릿에서 제공하는 기술입니다.
  • 현재 스프링에서는 필터나 인터셉터를 편의에 맞춰서 이미 구현하고 있는 것들이 많다고 합니다!

1-2. 시큐리티 필터

  • 시큐리티 필터는 시큐리티 프레임워크 내에서 동작할 수 있게 만들어둔 시큐리티 특정 필터라고 생각해주시면 됩니다.
  • 특히 그냥 필터를 상속받아 구현한 후 시큐리티 필터 동작에 끼워주고 싶을 떈 security config 내에서 동작 위치를 지정해주어야 합니다.(서블릿 필터를 시큐리티 필터처럼 사용하고자 함이기 때문에 일반적으로 동작 순서가 지정되어 있지 않기 때문에 위치 없이 등록하면 문제가 생겨요~)
  • @EnableWebSecurity(debug = true)를 해당 시큐리티 설정 클래스 레벨에 붙여주게 되면 로그로 찍혀서 해당 시큐리티 필터 체인이 어떤 순서로 진행되는지가 출력이 됩니다!(이건 개발때까지만 사용하고 실제 서버에 올리면 이 작업은 하지 않는 것이 좋다고 합니다~)

1-3. 시큐리티 필터의 동작

  • 시큐리티 필터의 경우 일반적으로 인증 작업(로그인)에 방법에 따라 상속할 클래스가 달라져요!
  • 기본적으로 폼 로그인을 사용해서 인증 작업을 구현할 경우엔 UsernamePasswordAuthenticationFiler를 사용합니다.
  • 여러개의 이미 구현된 클래스가 있을텐데 메서드의 기능에 따라서 동작시에 잘 작동할 수 있게 사용해주셔야 합니다!
public static class CustomUsernamePasswordFilter extends UsernamePasswordAuthenticationFilter {
    
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException {
      
      // TODO something...
      log.info("checked");
      
      return super.attemptAuthentication(request, response);
    }
  }
  • 해당 메서드의 경우엔 다음 단게인 authenticationManager로 unauthentication 객체를 넘겨주기 위한 메서드예요!
  • 이 경우엔 우리가 직접 필터를 상속 받아 메서드를 오버라이드 하고 있지만 기본으로 제공되는 걸 사용해도 무관합니다.

2. AuthenticationManager

2-1. AuthenticationManager?

  • 인증 처리를 진행할 때 authenticationfilter로부터 인증 처리를 위임 받는 첫번째 부분입니다.
  • 필터에서 넘겨 받은 id, pwd 값을 authenticationToken이라는 객체로 생성, 초기화 해서 넘겨 받고 manager로 넘겨 줍니다.
  • 이 부분은 직접 코드 작성을 해서 사용할 수도 있지만 일반적으로는 시큐리티 프레임워크가 이미 작성해둔 authenticationManager를 사용하시는 것이 더 안전한 방법입니다.
  • authenticationManager는 실제로 구현한 것이 ProviderManager 입니다. 이때 AuthenticationManager 인터페이스를 구현하면 커스텀 ProviderManager도 만들 수 있습니다.
  • authenticationManager는 기본적으로 인증을 이곳에서 진행하지 않고 authenticationProvider 측으로 인증 객체를 넘겨서 인증을 위임합니다!

3. AuthenticationProvider

3-1. 실제 인증 과정을 처리하는 provider

  • 실제 데이터베이스와의 맞닿아 있는 부분으로 해당 부분에서 입력받은 아이디 값과 패스워드 값을 저장된 회원의 아이디값과 패스워드 값과 일치하는지를 확인합니다.
  • authenticationProvide: 해당 부분에서 받아온 값과 입력한 값이 일치하는지 확인하는 부분으로 UserDetailsService를 이곳에서 사용해서 db에서 회원 유저값을 받아오셔야 합니다!
  • userDetailsService: 입력 받은 id값을 통해서 db에서 값을 받아 옵니다. 이때 User객체를 새로 생성해서 이를 넘겨줍니다.
  • UserDetails: 해당 객체는 사용자 정보를 담는 인터페이스 입니다.
    • userDetailsService
    • 해당 부분은 userService에서의 loadUserByUsername() 메서드의 리턴 타입으로 사용됩니다.

[로그인 유지를 위한 방식]

1. 세션 사용

  • 세션을 사용해서 로그인을 유지하는 방식이 있습니다.
  • 기본적으로 시큐리티에서는 세션을 사용해서 로그인을 유지하고 있습니다.
  • 그래서 인증이 성공한 후 작동하는 AuthenticationFilter.successfulAuthentication() 에서 context에 해당 authenticationToken 객체를 백엔드 넣어주면 전역으로 사용이 가능합니다!

2. jwt 사용

  • jwt를 사용하기 위해서는 일단 시큐리티 설정에서 세션을 사용하지 않는 설정을 넣어주어야 합니다.
  • http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); : 해당 설정은 스프링부트 2.7 버전에서 작성했습니다. 달라질 수 있으니 잘 찾아서 해당 버전에 맞춰서 사용하시길 ~
  • jwt를 사용하면 필터를 하나 더 달아줘야 합니다~ 이 작업이 있어야 로그인 유지와 같은 작업이 가능해집니다.(security context에 인증 객체를 넣어주는 행위) 대게 OncePerRequestFilter 를 상속 받아 구현하고 이 작업은 서블릿 필터를 시큐리티 필터처럼 사용하기 때문에 설정 파일을 위에서 언급한것처럼 순서를 잘 정해서 넣어주어야 합니다~
  • 그리고 이제 추가적으로 jwt를 자체적으로 저장할 수 있어야 합니다. rdb에 해당 토큰을 저장하면 I/O가 많아지기 때문에 이를 고려해서 redis 내의 저장하는 등의 방식을 사용합니다.
  • 로컬스토리지에 저장해도 괜찮고 탈취 문제가 있기 때문에 매 요청마다 리프레시 토큰으로 확인하는 식으로 진행합니다.
profile
즐겁고 괴로운 개발😎

0개의 댓글