WEB/Security

스프링 시큐리티 주요 아키텍처 이해

Tony Lim 2023. 1. 10. 14:22
728x90

위임 필터 및 필터 빈 초기화 - DelegatingFilterProxy, FilterChainProxy

DelegatingFilterProxy 는 servletContainer에서는 spring 기능을 사용하지 못하니까 요청을 FilterChainProxy에게 넘겨주는 역할을 담당한다.

apache tomcat의 ApplicationChainFilter에서 DelegatingFilterProxy로 넘겨주게된다.


필터 초기화와 다중 보안 설정

여러개의 FilterSecuirtyChain 을 만들어서 FilterChainProxy에 등록을 하는과정이다.
각각의 FSC는 antMatcher를 통해 어떤 resource를 담당할지 정하게 된다.

FilterChainProxy에서 등록된 여러 FSC중 RequestMatcher를 보고 알맞은 필터를 가지고 처리하게 된다.

 

https://tonylim.tistory.com/389

위에 최신 버전으로 나와있다. 


인증 저장소 - SecurityContextHolder, SecuirtyContext

SecuirtyContext

Authentication 객체가 저장되는 보관소로 필요시 언제든지 Authentication 객체를 꺼내어 쓸 수 있도록 제공되는 클래스

threadlocal에 저장되어 아무곳에서나 참조가 가능하도록 설계 , 인증이 완료되면 HttpSession에 저장되어 전역적인 참조가능

요청이 종료되고 나면 해당 request를 담당했던 스레드는  스레드풀로 반환되거나 스레드가 release될텐데 나중에 동일한 계정의 사용자가 요청을 해오면 Session에서 SecurityContext를 꺼내다가 다시 threadlocal에 저장하는 과정이 존재한다.

 

SecurityContextHolder

SecurityContext 객체 저장방식

MODE_THREADLOCAL = 스레드당 SecuirtyContext 객체를 할당, 기본방식
MODE_INHERITABLETHREADLOCAL = 메인 스레드와 자식 스레드에 관하여 동일한 SecurityContext를 유지
MODE_GLOBAL = 응용 프로그램에서 단 하나의 SecuirtyContext를 저장


인증 저장소 필터 - SecurityContextPersistenceFilter

client에게 응답하기전에 Session에 저장하고 SecuirtyContextHolder에서 제거된다.(어차피 스레드로컬이니까)

인증을 받았은 상태에서의 요청은 Session에서 SecuirtyContext를 꺼내와서 SecurityContextHolder에 넣어준다.


인증 흐름 이해 - Authentication Flow

 


인증 관리자: AuthenticationManager



인가 개념 및 필터 이해 = Authorization, FilterSecuirtyInteceptor

스프링 시큐리티가 지원하는 권한 계층

웹 계층 = url 요청에 따른 메뉴 혹은 화면 단위 레벨 보안 , /user
서비스계층 = 화면 단위가 아닌 메소드 같은 기능 단위의 레벨 보안 , user()
도메인 계층 (Access Control List(ACL) , 접근제어목록) = 객체 단위의 레벨 보안 

ACL은 어려운 개념이다.


https://www.youtube.com/watch?v=GTln3jc5_eg&ab_channel=LaurSpilca 

Class Table이 존재한다. 여기서는 Entity Product class를 1개row로 가지고 있다.

위 클래스를 기반으로 한 실제 객체table이 존재한다.

principle이 0 이면 sid는 Role을 의미하고 , 1이면 User를 의미한다. 

object identity table 에서는 어떤 클래스(object_id_class) 기반으로 만든 객체(object_id_identity)가
user(owner_sid)랑 매핑될 것인가 알려준다.

mask의 1 == read , 2 == write 를 의미한다. granting 이랑 같이 봐야한다. sid 1은 mask=1, granting=1이니 read만 가능하고 mask=2,granting=0 임으로 write는 불가능하다.

ace_order = john이 ADMIN 인데 어떤 특정 object에 admin만 접근 가능하고 john은 안된다하면 , john은 admin인데도 불구하고 접근이 불가능해진다.
반대가 되면 john이 접근 가능하고 admin은 불가능하다 순으로 되면 john만 접근 가능해진다.?

audit 은 로깅과 관련된 필드이다.

2개의 객체를 db에 지니고 있고 그중에 write 가 가능한 Beer 객체만 return 될 것이다.

ACL framework와 각종 설정을 해야한다. 뭔지만 알아두자


FilterSecurityInterceptor

마지막에 위치한 필터로서 인증된 사용자에 대하여 특정 요청의 승인/거부 여부를 최종적으로 결정
인증객체 없이 보호자원에 접근 -> AuthenticationException 발생
인증 후 자원에 접근 가능한 권한이 없을 떄 -> AccessDeniedException 발생
권한제어 방식중 HTTP 자원의 보안을 처리하는 필터
AccesDecisionManager가 실제 권한처리를 하게됨


인가 결정 심의자 - AccessDecisionManger , AccessDecisionVoter

AccessDecisionManger (Interface)

인증 정보, 요청 정보 , 권한 정보를 이용해서 사용자의 자원접근을 허용할 것 인지 거부할것인지를 최종 결정하는 주체

여러개의 Voter들을 가질 수 있으며 Voter들로부터 접근허용, 거부 , 보류 에 해당하는 각각의 값을 리턴받고 판단 및 결정

최종 접근거부시 예외 발생

위 인터페이스의 3가지 종류의 구현체이다.

 

AccessDecisionVoter

Voter가 권한 부여 과정에서 판단하는 자료

Authentication - 인증 정보 (user)
FilterInvocation - 요청 정보 (antMatcher("/user"))
ConfigAttributes - 권한정보 (hasRole("USER"))

결정 방식

ACCESS_GRANTED = 1
ACCES_DENIED = -1
ACESS_ABSTAIN = 0,접근보류 = Voter가 해당 타입의 요청에 대해 결정을 내릴 수 없는 경우


스프링 시큐리티 필터 및 아키텍처 정리

시나리오 - 인증을 시도하려는 사용자

현재 세션에 존재하지 않으니 SecurityContext가 존재하지 않으니
SecurityContextPersistenceFilter에서 빈 SecurityContext를 만들게된다. 

UsernamePasswordAuthenticationFilter에서 id ,pw 를 검증하면 SecurityContextHolder 안에 있는 SecurityContext에 인증된 Authentication 을 저장한다.

 

SessionManagementFilter = 세션이 없거나 , 세션에 SecurityContext가 없거나 하면 세션을 만들거나 , concurrent 체크를 하게된다. -> 이전 사용자의 세션을 만료시키는 정책이면 ConcurrentSessionFilter에서 이전사용자가 재요청시 걸려서 response.error를 뿜게 된다.

하지만 이 필터는 외부의 비정상적인 침입(계정만 탈취해서 접속하는 사용자등) 을 위한 필터이다. 실제 concurrent한 session checking은 

여기에서 일어나게 된다.


인증 부가 기능 - WebAuthenticationDetails, AuthenticationDetailsSource

기본적으로는 request에서 session , remoteAddress를 받아서 Authentication 객체에 넣어주지만 custom하게 만들어서 추가적인 정보를 넣을 수 있다.

public class FormWebAuthenticationDetails extends WebAuthenticationDetails {

    private String secretkey;

    public FormWebAuthenticationDetails(HttpServletRequest request) {
        super(request);
        this.secretkey = request.getParameter("secret_key");
    }

    public String getSecretkey() {
        return secretkey;
    }
}
@Component
public class FormAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {

    @Override
    public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new FormWebAuthenticationDetails( context );
    }

}
http
        .formLogin()
        .loginPage("/login")
        .loginProcessingUrl("/login_proc")
        .authenticationDetailsSource(authenticationDetailsSource)

설정시 추가해줘야한다.


728x90