본문 바로가기
Framework & Lib & API/스프링

스프링 시큐리티 개념 정리

by 코딩공장공장장 2023. 12. 6.

스프링 시큐리티란?

  • 스프링에서 제공해주는 인증, 인가, 보안에 대한 기능을 제공해주는 프레임워크
  • 인증, 인가, 보안 처리를 가진 각각의 필터들이 체인 형태를 이루며 순차적으로 처리
    (웹 컨테이너의 필터에서 동작, 즉, IOC 컨테이너 들어오기 전, 디스패처 서블릿 들어오기 전)
  • 스프링 프레임워크와 독립적으로 동작 가능

 

인증(Authentication)은 사용자의 신원을 확인하는 절차
인가(Authroization)는 인증된 사용자에게 자원에 대한 접근 권하을 부여하는 절차

 

 

스프링 시큐리티의 인증방식

Credential방식 : username, password를 이용한 방식(principal : 아이디, credential :비밀번호)

  • 폼 기반 로그인(아이디 비밀번호 입력하여 로그인)
  • 토큰 기반

 => 다양한 인증방식 있지만 모두 Authentication 객체로 표준화된 방식

 

Spring Security Architecture(구조)

 

1. 요청

2. 필터에서 요청정보를 통해 Authentication(UsernamePasswordAuthenticationToken)객체 생성

    => 해당 객체에 사용자가 요청한 username, password를 담음

3. Manager에게 인증 객체를 전달하면 Manager가 알맞은 Provider를 선정하여 처리를 넘김

4. Provider가 인증처리
  (RememberMeAuthenticationProvider, DaoAuthenticationProvider 같은 인증 처리 제공자)

5. DaoAuthenticationProvider의 경우 6번 진행

6. DB 검증 진행 후 User (아이디, 비번, 권한, enabled 정보 담고 있는 객체) 생성 

    => Authentication에 담겨있는 username과 password가 DB에 담겨있는 정보와 같은지 체크

10. SecurityContext에 Authentication객체 저장

 

Authentication(인증객체)

  • 사용자의 인증정보(principal : 아이디, credential : 비밀번호, 권한)를 저장하는 토큰
  • 구현체로 UsernamePasswordAuthenticationToken가 있음

 

SecurityContext

  • Authentication 객체 관리 목적
  • Authentication 객체를 포함하는 컨테이너
  • Thread별로 관리됨(ThreadLocal 변수로 참조)
  • 세션을 사용하는 방식에서는 세션에 저장됨
    => 따라서 세션을 사용한다면 세션에 접근하여 Authentication 정보를 가져올 수 있고
         세션을 사용하지 않는다면 SecurityContextHolder 통해서 접근
스프링시큐리티 세션 정책

ALWAYS
- default 방식으로 세션 사용
- 인증 처리 되면 세션에 SecurityContext를 저장하고 이후 세션에서 Authentication객체 접근하여 사용
- 마찬가지로 세션에 SecurityContext 존재하면 세션의 SecurityContext를 스레드 로컬에 매 요청마다 넣어줌

STATELESS : 세션 사용하지 않음.
- SecurityContext 매요청마다 생성해야함
- 세션에 SecurityContext 저장하지 않음

 

SecurityContextHolder

  • 현재 스레드에 대한 SecurityContext 관리 목적의 유틸리티 클래스 (정적 클래스)
  • SecurityContext 전략 설정(init) 및 SecurityContext에 대한 getter, setter, clearContext 함수 제공
    * clearContext 함수는 스레드 로컬에 남아있는 SecurityContext 제거 목적
       => 톰캣 was를 사용하는 경우 스레드풀을 사용하므로 SecurityContext가 스레드에 남아있으면 안됨
       => 따라서 clearContext는 세션 사용/미사용 관련없이  항상 호출됨
       => 세션 방식에서는 세션에서 SecurityContext가져와서 스레드 로컬에 넣어줌
  • SecurityContext 전략패턴
    1. MODE_THREADLOCAL : default 값, 스레드당 SecurityContext 객체를 할당합니다.
    2. MODE_INHERITABLETHREADLOCAL : 하위로 생성된 Thread에서 동일한 Context 공유
    3. MODE_GLOBAL : 애플리케이션의 모든 Thread가 단 하나의 SecurityContext만을 공유

 

스레드 로컬
스레드 단위로 로컬 변수를 제공하는 클래스
private : 객체 생성시 마다 초기화(스레드마다 독립적인 값 필요할 때)
private static : 객체 생성에도 초기화되지 않고 모든 스레드가 같은 값 공유

[사용법]
public class MyRunnable implements Runnable {
    private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
    @Override
    public void run() {
        threadLocal.set( (int) (Math.random() * 100D) );
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
        System.out.println(threadLocal.get());
    }
}

MyRunnable sharedRunnableInstance = new MyRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);​

 

 

[추가 개념]

서블릿 필터 vs DelgatingFilterProxy

  • 서블릿 필터는 웹 컨테이너(서블릿 컨테이너) 영역으로 스프링 IOC 컨테이너 관리대상인 스프링 빈 사용불가
  • DelegatingFilterProxy는 서블릿 필터로 블릿 컨테이너에 의해 생성되고 관리되지만, 스프링 컨테이너에 이름이 등록되어 컨테이너에 등록된 빈객체를 사용할 수 있다.

 

DelegatingFilterProxy 등록법

 

case1) 필터 이름을 springSecurityFilterChain으로 등록

<filter> 
   <filter-name>springSecurityFilterChain</filter-name>   
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

springSecurityFilterChain 이름으로 서블릿 컨테이너에 필터를 등록하면 스프링 컨테이너에서 해당 이름을 빈으로 등록한다. (컨테이너에 의해 참조될 수 있음)

 

case2) 커스터마이징한 이름으로 등록

[web.xml설정]
<filter> 
   <filter-name>myCustomFilter</filter-name>   
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

[security설정]
@Configuration
public class SecurityConfig {
    @Bean
    public DelegatingFilterProxy springSecurityFilterChain() {
        DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
        filterProxy.setTargetBeanName("myCustomFilter");
        return filterProxy;
    }
}

 

스프링 컨테이너에 DelegatingFilterProxy객체를 빈으로 등록하고 해당 빈의 targetBeanName 속성으로 서블릿 컨테이너에 등록한 필터의 이름을 지정함으로써 참조가능하게 설정(즉, wrapper클래스와 같은 개념)

 

 

스프링 시큐리티 필터

 

DelegatingFilterProxy는 springSecurityFilterChain 이름의 빈을 서블릿 컨테이너에서 찾아 요청 위임

(실제로 처리는 하지 않고 위임만 하는 Servlet Filter)

 

* springSecurityFilterChain 즉, FilterChainProxy는 서블릿 컨테이너에서 관리 되지 않지만 Filter인터페이스를 상속하고 있어서 필터처럼 동작

필터 설명
SecurityContext
PersistenceFilter
SecurityContextRepository에서 SecurityContext를 로드하고 저장하는 일을 담당함
SecurityContext객체 생성하고 세션에 저장, 세션에 저장된 컨텍스트 조회 및 참조
LogoutFilter 로그아웃 URL로 지정된 가상URL에 대한 요청을 감시하고 매칭되는 요청이 있으면 사용자를 로그아웃시킴
UsernamePassword
AuthenticationFilter
Authenticaion객체 저장
Concurrent
SessionFilter
동시 세션 관련 필터(한 사용자가 최대 가질 수 있는 세션 수, 즉 동시에 여러 디바이스에서 접속가능한 수 제어)
RememberMe
AuthenticationFilter
remember-me 기능 활성, 세션 만료시 쿠키내 암호화되어있는 값을 풀어 새로운 세션 생성
AnonymousAuthenticationFilter 이 필터가 호출되는 시점까지 인증 시도 하지 않았다면 AnonymouseAuthenticationToken 생성하여 컨텍스트에 저장
SessionManagementFilter 인증된 주체를 바탕으로 세션 트래킹을 처리해 단일 주체와 관련한 모든 세션들이 트래킹되도록 도움
ExceptionTranslationFilter 이 필터는 보호된 요청을 처리하는 동안 발생할 수 있는 기대한 예외의 기본 라우팅과 위임을 처리함
FilterSecurityInterceptor 이 필터는 권한부여와 관련한 결정을 AccessDecisionManager에게 위임해 권한부여 결정 및 접근 제어 결정을 쉽게 만들어 줌

 

How? 스프링 시큐리티는 웹 컨테이너 영역인 필터에서 동작하는데 어떻게 스프링 IOC 컨테이너 영역인 스프링 빈에 접근할 수 있나?

- 스프링 시큐리티는 시큐리티 필터를 springSecurityFilterChain라는 이름으로 등록한다.
- DelegatingFilterProxy는 springSecurityFilterChain라는 빈의 이름을 속성으로 가지고 있는다.(참조하기 위해서)
- DelegatingFilterProxy는 서블리 필터이지만 IOC 컨테이너에 등록된 빈이다.

즉, 위 3가지 동작방식을 통해 DelegatingFilterProxy가 스프링 IOC 컨테이너의 빈과 웹 컨테이너의 시큐리티 필터를 모두 참조 가능하게 됨으로써 논리적으로 다른 두 영역이 연결될 수 있는것이다. 

서블릿 컨테이너와 스프링 컨테이너는 물리적으로 분리되어있는가?

두 컨테이너는 논리적으로 다른 공간이지만 물리적으로는 같은 자바 프로그램 내에서 실행되는 JVM 메모리 영역을 공유하므로 하나의 객체만 연결되도 다른 객체들에 접근할 수 있다.

 

 

반응형