Spring Security - 정수원 7일차
10. 인가 결정 심의자 - AccessDesicionManager, AccessDesicionVoter
AccessDesicionManager(인터페이스)
FilterSecurityInterceptor
가 전달해준 인증정보, 요청정보, 권한정보를 이용해서 사용자의 자원접근을 허용할 것인지 거부할 것인지를 최종으로 결정하는 주체
- 여러개의 Voter를 가질 수있으며 Voter들로부터 접근허용, 거부, 보류에 해당하는 각각의 값을 리턴받고 판단 및 결정
- 최종 접근 거부 시 예외발생
구현체
- AffirmativeBased
- 여러개의 Voter클래스 중 하나라도 접근 허가로 결론을 내면 접근 허가로 판단
- ConsensusBased
- 다수표에 의해 최종 결정을 판단
- 동수일경우 기본은 접근 허가, allowIfEqualGrantedDeniedDecisions을 false로 하면 접근 거부
- UnanimousBased
- 모든 보터가 만장일치로 접근을 승인해야 하며 그렇지 않은 경우 접근을 거부한다
AccessDecisionVoter
- 판단을 심사하는 것(위원)
- Voter가 권한 부여 과정에서 판단하는 자료
- Aauthentication - 인증정보(user)
- FilterInvocation - 요청정보(antMatcher(”/user”))
- ConfigAttributes - 권한 정보(hasRole(”USER”))
- 결정 방식
- ACCESS_GRANTED : 접근 허용(1)
- ACCESS_DENIED : 접근 거부(-1)
- ACCESS_ABSTAIN : 접근 보류(0)
- Voter가 해당 타입의 요청에 대해 결정을 내릴수 없는 경우
- 접근 허용도 아니고 접근 거부도 아닐때
FilterSecurityInterceptor
가 인증 처리후 인증정보, 요청정보, 권한정보를 AccessDecisionManger
에게 전달한다.
- 그리고 구현체들에게 3개의 인증정보들을 넘기고 Voter들은 권한 판단 심사를 한다.
- 최종적으로 거부,승인,보류 중 한개를 리턴받고 Manager가 최종적으로 접근 권한 판단을 한다
- 승인이 되면
FilterSecurityIntercepter
한테 ACCESS_GRANTED를 보내준다
- ACCESS_DENIED받고 거부가 되면
AccessDeinedException
예외가 발동하면서 ExceptionTranslationFilter
가 처리하도록 한다.
11. 스프링 시큐리티 필터 및 아키텍처 정리
- SecurityConfig의 설정클래스를 두개 만든다.
- 설정한 api와 구성대로 Filter들을 생성한다 ( HttpSecurity 클래스가 생성 )
- 각각의 설정들로 만든 Filter들을 WebSecurity가 받는다
- WebSecurity가 FilterChainProxy 빈 객체를 생성함, 그때 생성자로 자기가 전달받은 filter들을 전달
- 이때 DelegatingFilterProxy 서블릿필터가 springSecurityFilterChain라는 빈을 찾는다. 그게 바로 FilterChainProxy이다
- DelegatingFilterProxy는 사용자가 요청을 하면 받아서 FilterChainProxy를 찾아 위임한다.
- 여기까지가 초기화 과정
- 사용자가 인증을 시도하는 경우, 인증 후 자원에 접근하는 경우
- 먼저 인증을 시도하는 경우
- 사용자가 인증을 시도하면 DelegatingFilterProxy가 먼저 요청을 받는다. 그리고
FilterChainProxy
에게 위임 한다. FilterChainProxy
는 초기화때 이미 받은 filter를 가지고있고 그것들로 처리한다
- 먼저 SecurityContextPersistenceFilter가 사용자 요청을 받아서 처리한다.
- HttpSessionSecurityContextRepository 클래스가 Security 객체를 생성하고 Session에 저장,조회 등을 한다
- 사용자가 이전에 Security객체를 세션에 저장한 이력이 있는지 체크한다.
- 없기 떄문에 create SecurityContext 새로운 시큐리티 컨텍스트를 생성한다. ( 처음 인증을 요청을 했거나, annanimous 사용자 일경우 )
- 그다음 LogoutFilter 인데 로그아웃 요청을 하지 않을 경우 특별히 안씀
- UsernamePasswordAuthenticationFilter인데 실질적으로 form 로그인 방식을 처리하는 필터이다
- Authentication 인증객체를 만들어서 아이디 패스워드를 저장시킨다
- Authentication Manager 인증 관리자에게 인증 처리를 맡긴다.
- Authentication Manager가 Authentication Provider 에게 실제 처리를 위임한다
- Authentication Provider 가 UserDetailService클래스를 활용해서 아이디와 패스워드를 검증한다.
- 인증이 성공하면 SecurityContextHolder안에 SecurityContext안에 Authentication객체를 생성하고 SecurityContext안에 저장한다.
- 결국 SecurityContextPersistenceFilter가 생성하고 저장한 객체를 참조를 하는 것이고 인증에 성공한 최종적인 객체를 저장한다
- 인증객체를 성공적으로 생성하면서 SessionManagementFilter가 동시에 인증처리를 한다.
- 인증에 성공했다면 ConcurrentSession가 성공한 계정으로 이전에도 요청이 있었는지 체크를 한다. 정책상 동일한 계정으로 로그인을 1개로 제한했다면, 지금은 최초 접속이니 넘어가고
- SessionFixation(세션고정보호) 인증을 한시점에서 새로운 쿠키가 발급된다. 인증 이전의 쿠키는 없어지고 다시금 세션이 생성되고 쿠키가 발급되도록 한다
- Register SessionInfo 이제 사용자가 하나 등록되었다.
- SessionManagementFilter 처리가 인증을 하고 있는 시점에 같이 처리한다.
- 인증 성공하면 루트페이지로 가는 시점에 다시금 SecurityContextPersistenceFilter가 세션에 최종적으로 인증에 성공한 객체를 저장한다.
- 인증에 성공한 사용자가 페이지를 요청하면 다시 DelegatingFilterProxy를 통해 요청을 전달받고 SecurityContextPersistenceFilter 필터가 동적하고 load Context가 작동하면서 인증객체를 세션에서 SecurityContext에 저장한다. create SecurityContext를 할 필요없이
- 마지막 최종적으로 응답을 할때 ClearSecurityContext를 하면서 SecurityContext를 삭제를 한다
- LogFilter랑 UsernamePasswordAuthenticationFilter는 건너뛰고 Concurrent SessionFilter에 가는데 동일한 계정이 두개 이상일 경우 발동한다(계정정책은 설정하기 나름)
- RemeberMe AuthenticationFilter는 현재 사용자가 세션이 만료되거나 무효되어서 SecurityContext가 NULL일 경우 작동한다. ( 인증을 받은 사용자에게는 작동안함 )
- 인증객체가 NULL일 경우에 요청 헤더에 remember me cookie를 담고 있을 경우 동작한다.
- Anonymous AuthenticationFilter는 사용자가 인증 시도도 하지않고 권한도 없이 어떤 자원에 바로 다이렉트로 접속을 시도할 경우 이필터가 동작
- Anonymouse Token을 만들어서 SecurityContext객체에 저장하는 역할을 한다. 익명인지 인증인지 필터링 하는 역할
- SessionManagementFilter 는 현재 SecurityContext가 없거나 Null일 경우 동작한다
- 이 이후부터는 인증 이후의 큰 역할을 하는 필터 들이다
- ExceptionTranslationFilter는 인증이나 인가 예외가 발생했을 때 동작
- FilterSecurityInterceptor 에서 유저 인증부터 체크를 하고 예외가 발생하면 ExceptionTranslationFilter에게 예외를 던진다.
- 나머지 AccessDecisionManager 와 AccessDecisionVoter에서 인가 예외가 발생 하면 AccessDenied Exception을 던진다
- 이제 두번째 사용자가 같은 아이디와 패스워드로 인증을 요청한다.
- UsernamePassword AuthenticationFilter를 거쳐서 인증까지 완료 하고 정책에 따라 처리한다. SessionAuthenticationException은 현재 사용자 인증을 차단을 하고, Session expireNow는 이전 세션 사용자를 만료 시킨다.
- 만약 expireNow정책을 펼쳐서 이전 사용자 세션을 만료를 시키고 사용자1이 재요청을 할때 ConcurrentSessionFilter가 동작해서 예외를 발생시킨다