세션 처리
SecurityContextPersistenceFilter
- SecurityContextRepository 인터페이스 구현체를 통해 사용자의 SecurityContext를 가져오거나 갱신함
- 인증 관련 필터 중 가장 최 상단에 위치 — 이미 인증된 사용자는 다시 로그인할 필요가 없음
- SecurityContext가 존재하지 않는다면, empty SecurityContext를 생성함
- SecurityContextRepository 인터페이스 기본 구현은 Session을 이용하는 HttpSessionSecurityContextRepository 클래스
SessionManagementFilter
- 세션 고정 보호 (session-fixation protection)
- session-fixation attack — 세션 하이재킹 기법중 하나로 정상 사용자의 세션을 탈취하여 인증을 우회하는 기법
- 인증 전에 발급 받은 세션 ID가 인증 후에도 동일하게 사용되면 발생할 수 있음
- 즉, 인증 전에 사용자가 가지고 있던 세션이 인증 후에는 사용되지 않도록 하면 해당 공격에 효과적으로 대응할 수 있음
- 새로운 사용자가 로그인 되었을 때 기존 세션을 SessionAutheticationStrategy 인터페이스를 통해 세션을 새로 만든다.
- 로그인 이후에는 새로 생성된 세션을 사용.
- 악의적인 공격자가 이전에 갖고 있던 세션 아이디는 유효하지 않게 되고, 방어가 가능하다.
- Spring Security에서는 4가지 설정 가능한 옵션을 제공함
- none — 아무것도 하지 않음 (세션을 그대로 유지함)
- newSession — 새로운 세션을 만들고, 기존 데이터는 복제하지 않음
- migrateSession — 새로운 세션을 만들고, 데이터를 모두 복제함
- changeSession — 새로운 세션을 만들지 않지만, session-fixation 공격을 방어함 (단, servlet 3.1 이상에서만 지원. 스프링 시큐리티에서 기본으로 사용하는 방법.)
- 유효하지 않은 세션 감지 시 지정된 URL로 리다이렉트 시킴
- 세션 생성 전략 설정
- IF_REQUIRED — 필요시 생성함 (기본값)
- NEVER — Spring Security에서는 세션을 생성하지 않지만, 세션이 존재하면 사용함
- STATELESS — 세션을 완전히 사용하지 않음 (JWT 인증이 사용되는 REST API 서비스에 적합)
- IF_REQUIRED — 필요시 생성함 (기본값)
- ALWAYS — 항상 세션을 사용함
- 동일 사용자의 중복 로그인 감지 및 처리
- maximumSessions — 동일 사용자의 최대 동시 세션 갯수
- maxSessionsPreventsLogin — 최대 갯수를 초과하게 될 경우 인증 시도를 차단할지 여부 (기본값 false)
@Override
protected void configure(HttpSecurity http) throws Exception {
http
/**
* 세션 관련 설정
*/
.sessionManagement()
.sessionFixation().changeSessionId()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/")
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
.and()
.and()
;
}
- AbstractAuthenticationProcessingFilter 객체는 SessionManagementFilter와 동일한 세션 고정 보호, 최대 로그인 세션 제어를 수행함
- 위 두 개의 필터는 SessionAuthenticationStrategy 객체를 공유함
- AbstractAuthenticationProcessingFilter 구현을 보면, 인증 처리가 완료 된 후 SessionAuthenticationStrategy 객체를 통해 필요한 처리를 수행하고 있음
인가 처리 (AccessDecisionManager)
인가(Authorization)
: 어플리케이션 보안을 이해하는데 두 번째로 중요한 핵신 개념으로(다른 하나는 인증) 권한이 부여된 사용자들만 특정 기능 또는 데이터에 접근을 허용하는 기능이다.
이를 위해 인가 처리는 두 개의 작업으로 구분된다.
- 인증된 사용자와 권한을 매핑해야 함 > Spring Security에서는 보통 역할이라고 함 (예: ROLE_USER, ROLE_ADMIN, ROLE_ANONYMOUS)
- 보호되는 리소스에 대한 권한 확인 > 관리자 권한을 가진 사용자만 관리자 페이지에 접근 가능
FilterSecurityInterceptor
- 필터 체인 상에서 가장 마지막에 위치하며, 사용자가 갖고 있는 권한과 리소스에서 요구하는 권한을 취합하여 접근을 허용할지 결정함
- 실질적으로 접근 허용 여부 판단은 AccessDecisionManager 인터페이스 구현체에서 이루어짐
- 해당 필터가 호출되는 시점에서 사용자는 이미 인증이 완료되고, Authentication 인터페이스의 getAuthorities() 메소드를 통해 인증된 사용자의 권한 목록을 가져올수 있음
- 익명 사용자도 인증이 완료된 것으로 간주하며, ROLE_ANONYMOUS 권한을 갖음
- 보호되는 리소스에서 요구하는 권한 정보는 SecurityMetadataSource 인터페이스를 통해 ConfigAttribute 타입으로 가져옴
- FilterSecurityInterceptor부터 인가처리 시작.
SecurityContextHolder를 통해 인증된 사용자의 Authentication객체를 가져옴. 어선티케이션 객체는 사용자가 익명 사용자라면 AnonymousAuthenticationToken, 인증 완료됐다면 UsernameAuthenticationToken 또는 RemembermeAuthenticationToken일 것.
- SecurityMetadataSource를 통해 사용자가 접근하려고 하는 리소스에서 요구하는 권한이 뭔지에 대한 정보를 ConfigAttributesType으로 가져옴. AccessDecisionManager(인터페이스 타입)를 호출. 액세스 디시젼 매니저의 구현체에는 세가지가 있음.
디시젼 매니져가 Voter들을 통해 승인할지 거절할지 판단하고 구현체가 그 보터들의 판단결과를 취합해서 최종적으로 승인, 거절을 결정. 승인할 경우 사용자는 리소스에 접근할 수 있게 되고, 처리가 정상 종료됨. 만약 권한이 없어 거절당하면 엑세스 디나이드 익셉션 발생.ㅣ
AccessDecisionManager 인터페이스
- 사용자가 갖고 있는 권한과 리소스에서 요구하는 권한을 확인하고, 사용자가 적절한 권한을 갖고 있지 않다면 접근 거부 처리함
- AccessDecisionVoter 목록을 갖고 있음
- AccessDecisionVoter들의 투표(vote)결과를 취합하고, 접근 승인 여부를 결정하는 3가지 구현체를 제공함
- AffirmativeBased — AccessDecisionVoter가 승인하면 이전에 거부된 내용과 관계없이 접근이 승인됨 (기본값)
- ConsensusBased — 다수의 AccessDecisionVoter가 승인하면 접근이 승인됨
- UnanimousBased — 모든 AccessDecisionVoter가 만장일치로 승인해야 접근이 승인됨
AccessDecisionVoter 인터페이스
- 각각의 AccessDecisionVoter는 접근을 승인할지 거절할지 혹은 보류할지 판단함 (vote 메소드)
- ACCESS_GRANTED — 접근 승인
- ACCESS_DENIED — 접근 거부
- ACCESS_ABSTAIN — 판단 보류 (판단을 위한 정보 부족 등)
int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = -1;
int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
WebExpressionVoter 구현체
- SpEL 표현식을 사용해 접근 승인 여부에 대한 규칙을 지정할 수 있음
- SpEL 표현식 처리를 위해 DefaultWebSecurityExpressionHandler 그리고 WebSecurityExpressionRoot 구현에 의존함
- DefaultWebSecurityExpressionHandler.createSecurityExpressionRoot() 메소드에서 WebSecurityExpressionRoot 객체를 생성함
- WebSecurityExpressionRoot 클래스는 SpEL 표현식에서 사용할수 있는 다양한 메소드를 제공