Spring Security 는 사실상 Filter로 동작한다.
본인 입맛에 따라서 필터를 추가하거나 제거해서 사용할 수 있다.
많이 쓰이는 필터는 아래와 같다.
BasicAuthenticationFilter
UsernamePasswordAuthenticationFilter
csrfFilter
RememberMeAuthenticationFilter
AnonymousAuthenticaionFilter
FilterSecurityInterceptor
ExceptionTranslationFilter
SecurityContext 에 대해서 저번 글에서 살짝 알아보았다.
Security Context란??
SecurityContext는 Authentication (인증) 을 보관하는 역할을 하며, SecurityContext를 통해 Authentication 객체를 꺼내올 수 있다.
SecurityContextPersistenceFilter는 보통 두번쨰로 실행된다.
SecurityContext 를 찾아와서 ContextHolder 에 넣어주는 역할을 한다.
위 메소드(너무 길어서 생략)가 SecurityContext 를 가져오거나 새로 만든다고 하는데 , 기본적으로 HttpSession 에서 가져온다.
(쿠키를 사용한다고 한다.)
우리가 평소에 알고있는 인터넷 브라우저의 쿠키가 방금 언급한 쿠키와 같다.
유저가 로그인하면 Session은 유저와 브라우저에게 쿠키를 전달해 저장하는것이다.
크롬 브라우저 확장 프로그램중에 EditThisCookie 라는 확장 프로그램이 있다.
해당 사이트를 접속시에 어떠한 쿠키의 정보를 갖고있는지 출력 또는 쿠키 삭제까지 가능하다.
해당 프로그램으로 쿠키를 삭제하면, 로그아웃이 된다.
세션을 활용한 여러 기능들도 해당 SecurityContext 를 주체로 구현이 가능하다.
말그대로 기본적인 인증을 허락해주는 필터이다.
인증을 받지 않은 상태에서 접근하는 요청들을 일회성으로 허락해준다
예를 든다면 , 로그인을 해야지 접근이 가능한 게시글 작성 페이지를 해당 필터로 로그인 과정 없이 로그인 데이터를 Base64로 인코딩해서 모든 요청에 포함해서 보내면 일회성 인증을 받아온다.
세션이 필요없고 요청이 들어올때마다 인증이 이루어진다. 출입문의 도어락과 비슷하다고 보면 된다.
보안에 취약하기 떄문에 반드시 https 를 사용하도록 권장된다.
(사용하지 않는다면 명시적으로 disable 시켜주는게 좋다.)
아우 길어!!! 😡
Form 데이터로 username,password 기반의 인증을 담당하고 있는 필터이다.
UsernamePasswordAuthenticationFilter
-> ProviderManager -> AbstractUserDetailsAuthenticationProvider ->
DaoAuthenticationProvider -> UserDetailsService
순으로 인증 과정을 거친다.
인증이 완료되고 UserDetailsService 의 정보들로 세션을 활용할 수 있다.
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
Optional<UserAccount> _account = userAccountRepository.findById(userId);
if (_account.isEmpty()) {
throw new UsernameNotFoundException("사용자를 찾을수 없습니다.");
}
UserAccount account = _account.get();
List<GrantedAuthority> authorities = new ArrayList<>();
if ("test".equals(userId)) {
authorities.add(new SimpleGrantedAuthority(UserAccountRole.ADMIN.getValue()));
authorities.add(new SimpleGrantedAuthority(UserAccountRole.USER.getValue()));
} else {
authorities.add(new SimpleGrantedAuthority(UserAccountRole.USER.getValue()));
}
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return new BoardPrincipal(account.getUserId(), account.getUserPassword(), authorities, account.getEmail(), account.getNickname(), account.getMemo());
}
이런식으로 인증 절차를 거친후 UserDetailsService 에서 인가 까지 할 수 있다.
ProviderManager 는 Password 가 일치하는지, 계정이 활성화 되어있는지 확인한 후에 authentication 을 반환한다.
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
CsrfFIlter 는 csrfAttack 을 방어하는 필터이다.
이런 공격을 방어하기 위해 CSRF 토큰을 활용해 악의적인 CsrfAttack을 방어한다.
위조된 페이지는 CsrfToken 이 없거나 잘못된 CsrfToken 을 가지고 있다.
ThymeLeaf에선 페이지를 만들때 자동으로 Csrf Token을 넣어준다.
(태그 안에 hidden 으로 자동으로 생성된다.)
/ 자동으로 활성화 되어있는 Filter 이다.
흔히 알고있는 로그인 유지하기 기능을 활성화 시켜주는 필터이다.
기존 세션의 만료 시간은 기본 설정이 30분이지만, RememberMe 를 사용하면 2주동안 세션 유지가 가능하다.
기존 세션과 다르게 Rememberme 전용 세션이 활성화된다. (쿠키가 따로 생성된다.)
적용 또한 굉장히 간단하다 .
<div>
<span>로그인 유지하기</span>
<input type="checkbox" name="remember-me" id="remember-me">
</div>
로그인 폼에 이 태그만 추가해줘도 활용이 가능하다.
인증이 안된 유저가 요청을 하면 익명 유저로 만들어 Authentication 에 넣어주는 필터이다.
인증이 되지 않아도 Null 이 아니라 기본 Authentication 을 만들어주는 개념이다.
해당 필터로 인해 단순 유저 혹은 null 이 아닌 로그인 하지 않은 상황에서의 인증 처리도 구현이 가능하다.
FilterSecurityInterceptor 는 위의 메소드로 인해 넘어온 authentication 의 내용을 기반으로 최종 인가 판단을 내린다.
대부분 필터중에 뒤쪽에 위치한다.
인증을 가져와서 문제가 있다면 AuthenticationException 을 발생시키고, 문제가 없다면 해당 인증으로 인가를 판단한다.
인가가 거절된다면 AccessDeniedException 이 발생되고, 승인된다면 정상적으로 필터가 종료된다.
말그대로 위의 메소드에서 발생할 수 있는 예외들을 처리해주는 필터이다.
인증 인가에 실패했을때 어떤 행동을 취해야하는지 결정해주는 filter 이다.
예를 들자면, 로그인 폼에서 해당 필터로 인해 스프링 시큐리티는 인증이 실패했을때 함께 다시 로그인폼으로 Redirect 해주지만, 필터가 적용되지 않는다면 에러가 발생하며 Whitelabel Error Page 를 보여줄것이다.
다음 글에서는 해당 필터들을 적용해 SecurityConfig 클래스를 작성해보자.