스프링 시큐리티 주요 아키텍처
Categories: security
Tags: authentication, db, security
스프링 시큐리티와 서블릿 필터
**- 서블릿 필터에서 스프링 빈으로 처리를 넘기는 과정 **
서블릿 필터는 서블릿 컨테이너에서 생성 및 관리하는 필터이고, 스프링 빈은 스프링 컨테이너에서 관리된다. 스프링 위에서 인증, 인가를 처리하기 위해서는 서블릿 필터가 아닌 스프링으로 요청을 넘겨야 한다. 이 때, 서블릿 필터는 DelegatingFilterProxy클래스를 사용하여 스프링 빈에게 요청을 위임한다. springSecurityFilterChain 이름으로 생성된 빈을 ApplicationContext 에서 찾아 요청을 위임하고, 실제 보안처리를 하지 않는다.
FilterChainProxy
DelegatingFilterProxy로 부터 요청을 받고 실제 보안을 처리하는 곳이다.
FilterChainProxy에서는 각 필터들을 순서대로 호출하며 인증 / 인가에 대한 요청을 처리한다. 이 때 스프링에서는 springSecurityFilterChain라는 이름으로 빈이 생성된다.
사용자 정의 필터를 생성해서 기존의 필터 전,후로 추가가 가능하고 그렇기 때문에 필터의 순서를 잘 정의해야 한다. 마지막 필터까지 인증 및 인가 예외가 발생하지 않으면 보안 통과된다.
필터 초기화 및 다중필터
설정 클래스별로 보안 기능을 따로 구현할 수 있다. 각각의 클래스마다 RequestMacher를 설정하고 별도로 각각 필터가 생성된다.
Authentication
- 사용자의 인증 정보를 저장하는 객체
- 인증시 id와 password를 담고 인증 검사를 위해 전달되어 사용된다.
- 인증후 user객체, 권한정보를 담고 SecurityContext에 저장되어 전역적으로 사용된다.
Authentication authentication =
SecurityContexHolder.getContext().getAuthentication();
Authentication 객체의 구조
- principal: 사용자 아이디 혹은 User객체
- credentials: 사용자 비밀번호
- authorities: 인증된 사용자의 권한 목록
- details: 인증 부가 정보
- Authenticated: 인증 여부(Bool)
인증 흐름 과정
- Login Request로 username과 password를 전달
- usernamePasswordAuthenticationFilter(인증필터)가 요청정보를 받아서 정보 추출을 하여 인증객체 (Authentication)을 생성
- AuthenticationManager가 인증객체를 가지고 인증처리를 수행한다. 인증이 실패하면 예외 발생.
- 인증이 성공 했다면 Authentication 인증객체를 만들어서 Principal, Credentials, Authorities, Authenticated 들을 넣어서 반환
- SecurityContextHolder객체 안의 SecurityContext에 저장한다.
SecurityContextHolder & ThreadLocal
SecurityContextHolder는 ThreadLocal로 저장되기 때문에, 쓰레드 마다 각각 다른 SecurityContextHolder 인스턴스를 가지고 있기 때문에 사용자별로 각기 다른 인증 객체를 가질 수 있다.
인증객체 저장 과정
인증과정의 프로세스
- Login Request
2.Server가 요청을 받아서 Thread를 생성 (ThreadLocal을 가지고 있음)
3.인증 처리 시도 → 인증 객체(Authentication) 생성
- 인증 실패 경우: SecurityContextHolder.clearContext() 인증객체 초기화
- 인증 성공 경우: SecurityContextHolder안의 SecurityContext에 인증객체(Authentication)저장, ThreadLocal에 저장되어 있음.
4.SecurityContext에서 HttpSession에 저장.
인증객체 저장 과정 상세 프로세스
- 인증 전
- SecurityContextHolder 생성
- 인증 필터가 인증 처리
- 인증 완료되면 SecurityContext 객체 안에 저장
- Session에 SecurityContext 저장
- SecurityContext 제거 후 응답
- 인증 후
- Session에 SecurityContext 확인
- SecurityContext 꺼내서 SecurityContextHolder에 저장
인증 흐름
- Client 에서 로그인 요청
- UsernamePasswordAuthenticationFIlter에서 ID + PASSWORD를 담은 인증객체(Authentication)를 생성
- AuthenticationManager에게 인증객체를 넘기며 인증처리를 위임
4.AuthenticationManager는 적절한 AuthenticationProvider에게 인증처리를 위임
5.Provider에서 id, password를 가지고 실제 인증 처리 역할을 한다.
- loadUserByUsername(username)메서드를 호출해서 유저객체를 요청한다.
- UserDetailsService 인터페이스에게 loadUserByUsername(username) 요청
- Repository에 findById() 메서드로 유저 객체 조회를 한다. 유저객체가 존재하지 않으면 UsernameNotFoundException이 발생하고 UsernamePasswordAuthenticationFIlter에서 예외를 처리한다. → FailHandler()에서 후속처리 유저객체가 존재한다면 UserDetails 타입으로 반환된다.
6.AuthenticationManager는 Password를 검증. 인증객체의 Password와 반환받은 UserDetails의 Password를 비교한다.
- 일치하지 않을 경우 BadCredentialException 발생 후 인증 실패
- 성공한 경우 user정보 + 권한이 담긴 Authentication 객체를 UsernamePasswordAuthenticationFilter에 전달한다.
7.SecurityContext에 저장한다. 8.이후 전역적으로 SecurityContextHolder에서 인증객체를 사용가능하게 된다.
AuthenticationManager의 인증 위임
AuthenticationManager는 필터로부터 인증처리요청을 받으면, ProviderManager를 통해 현재 인증처리를 할 수 있는 AuthenticationProvider를 ProviderManager를 통해 찾는다.
AuthenticationProvider
AuthenticationProvider 는 인터페이스이고 구현체를 만들기 위해 다음 메서드를 구현해야 한다.
- authenticate(authentication) : 실제적인 인증처리를 위한 검증 메서드
- supports(authentication): 이 Provider는 어떤 인증처리를 지원하는지 알려주는 메서드. 그래야 ProviderManager가 찾을 수 있다.
authenticate(authentication)
- 아이디 검증
- UserDetailsService에서 사용자 정보를 조회한다.
- 존재할 경우 UserDetails 타입으로 반환한다.
- 존재하지 않을 경우 UserNotFoundException 발생
- 패스워드 검증
반환된 UserDetails에 저장된 password와 로그인시 입력한 패스워드가 일치하는지 비교한다.
- 일치하지 않을 경우 BadCredentialException 발생
- Password Encoder를 이용해 두 암호를 비교한다.
- 추가 검증 추가적으로 사용자가 정의한 검증 조건 검증
Leave a comment