개발자의 오르막
[SpringSecurity #11] Security Auth 관련 Filter 본문
# UsernamePasswordAuthenticationFilter
: 폼 로그인을 처리하는 인증 필터
- 사용자가 폼에 입력한 username 과 password로 Authentication을 만들고
AuthenticationManager를 사용하여 인증을 시도한다.
- AuthenticationManager (ProviderManager) 는 여러 AuthenticationProvider 를 사용하여
인증을 시도하는데, 그 중에 DaoAuthenticationProvider 는 UserDetailsService를 사용하여
UserDetails 정보를 가져와 사용자가 입력한 password 와 비교한다.
# DefaultLoginPageGeneratingFilter
- 기본 로그인 폼 페이지를 생성해주는 필터, Get /login 요청을 처리하는 필터
http.formLogin().loginPage("/login");
이 설정을 추가하면 커스텀 한 로그인 페이지로 이동하기 때문에, DefaultLoginPageGeneratingFilter 를
사용하지 않게 된다.
# BasicAuthenticationFilter
- Basic 인증
- 요청 헤더에 username 와 password를 실어 보내면 브라우저 또는 서버가 그 값을 읽어서 인증하는 방식
예) Authorization : Basic QWxhZGRpb~ ( Base64 로 username, password 를 인코딩 )
- 보통 브라우저 기반 요청이 클라이언트의 요청을 처리할 때 자주 사용
- 보안에 취약하기 때문에 반드시 HTTPS를 사용할 것을 권장
# RequestCacheAwareFilter
- 현재 요청과 관련 있는 캐시된 요청이 있는지 찾아서 적용하는 필터
캐시된 요청이 없다면, 현재 요청 처리
캐시된 요청이 있다면, 해당 캐시된 요청 처리
# SecurityContextHolderAwareRequestFilter
- 시큐리티 관련 서블릿 API를 구현해주는 필터
HttpServletRequest#authenticate(HttpServletResponse)
HttpServletRequest#login(String, String)
HttpServletRequest#logout()
AsyncContext#start(Runnable)
# AnonymousAuthenticationFilter
- 현재 SecurityContext에 Authentication 이 null 이면 "익명 Authentication" 을 만들어
넣어주고, null 이 아니면 아무일도 하지 않는다.
- 기본으로 만들어 사용할 "익명 Authentication" 객체를 설정할 수 있다.
http.anonymous()
.principal()
.authorities()
.key()
# SessionManagementFilter
- 세션 변조 방지 전략 설정 : sessionFixation
세션 변조
none
newSession
migrateSession (서블릿 3.0- 컨테이너 사용시 기본값)
→ 인증이 됐을 때 새로운 세션을 만들고, 기존 세션의 정보를 복사해오는 것
changeSessionId (서블릿 3.1+ 컨테이너 사용시 기본값)
→ 스프링 부트를 사용하기 때문에 내장된 톰캣 서블릿 4.0 버전을 사용하고 있음
→ 세션 ID 만 바꾸기 때문에 migrateSession 보다 빠름
SecurityConfig 파일에서 세션 전략을 지정할 수 있다.
http.sessionManagement()
.sessionFixation().changeSessionId();
http.sessionManagement()
.sessionFixation().migrateSession();
- 유효하지 않은 세션을 리다이렉트 시킬 URL 설정
http.sessionManagement()
.sessionFixation()
.changeSessionId()
.invalidSessionUrl("/login");
- 동시성 제어 : maximumSessions
추가 로그인을 막을지 여부 설정 (기본값, false)
→ 한 계정을 여러명이 사용할 수 있음 (기본값)
http.sessionManagement()
.sessionFixation()
.changeSessionId()
.maximumSessions(1)
.maxSessionsPreventsLogin(true);
maximumSessions 를 1로 설정함으로써 동시 로그인 유저 수를 1명으로 제한 할 수 있다.
그리고 maxSessionsPreventsLogin 은 false 인 경우 기존 로그인 세션을 만료 시키고 새로운 로그인 인증만
사용하고, true 인 경우는 기존 로그인 세션만 유지하고, 새로운 로그인 세션을 막아주는 설정이다.
- 세션 생성 전략 : sessionCreationPolicy
IF_REQUIRED (기본값, 필요하면 만든다)
NEVER (시큐리티에서는 생성 X, 기존의 세션을 활용함)
STATELESS (세션을 사용하지 않음)
ALWAYS (세션을 항상 사용)
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
* RequestCacheAwareFilter 도 세션을 유지해야만 제대로 동작함
# ExceptionTranslationFilter
- FilterSecurityInterceptor (AccessDecisionManger, AffirmativeBased) 와 밀접한 관계가 있음
→ ExceptionTranslationFilter 가 FilterSecurityInterceptor 보다 이전에 있어야 함.
→ ExceptionTranslationFilter 가 FilterSecurityInterceptor 를 감싸고 실행되어야 함. ( try-catch 구문 )
→ Authentication 에러 ( AuthenticationEntryPoint 를 활용해서 처리 ) 로그인 페이지로 이동함으로써 처리
AccessDeniedException 에러 ( AccessDeniedHandler를 활용해서 처리 ) 403 Error 로 처리
- 에러페이지 커스텀 설정
http.exceptionHandling()
.accessDeniedPage("/access-denied");
# FilterSecurityInterceptor
- HTTP 리소스 시큐리티 처리를 담당하는 필터. AccessDecisionManager 를 사용하여 인가를 처리한다.
http.authorizeRequests()
.mvcMatchers("/", "/info", "/account/**", "/signup").permitAll()
.mvcMatchers("/admin").hasRole("ADMIN")
.mvcMatchers("/user").hasRole("USER")
.anyRequest().authenticated()
.expressionHandler(expressionHandler());
# RememberMeAuthenticationFilter
- 세션이 사라지거나 만료가 되더라도 쿠키 또는 DB를 사용하여 저장된 토큰 기반으로
인증을 지원하는 필터
- RememberMe 설정
http.rememberMe()
// 폼에서 remember me 값을 안보내줘도 remember me 사용
.alwaysRemember(false)
// remember Me 파라미터 이름 설정
.rememberMeParameter("remember")
// 토큰 유효기간 설정, 기본값 2주
.tokenValiditySeconds(1)
// 쿠키 보안 설정
.useSecureCookie(true)
.userDetailsService(accountService)
.key("remember-me-sample");
- RememberMeAuthenticationFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (SecurityContextHolder.getContext().getAuthentication() == null) {
Authentication rememberMeAuth = rememberMeServices.autoLogin(request,
response);
if (rememberMeAuth != null) {
// Attempt authenticaton via AuthenticationManager
try {
rememberMeAuth = authenticationManager.authenticate(rememberMeAuth);
// Store to SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
onSuccessfulAuthentication(request, response, rememberMeAuth);
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder populated with remember-me token: '"
+ SecurityContextHolder.getContext().getAuthentication()
+ "'");
}
// Fire event
if (this.eventPublisher != null) {
eventPublisher
.publishEvent(new InteractiveAuthenticationSuccessEvent(
SecurityContextHolder.getContext()
.getAuthentication(), this.getClass()));
}
if (successHandler != null) {
successHandler.onAuthenticationSuccess(request, response,
rememberMeAuth);
return;
}
}
catch (AuthenticationException authenticationException) {
if (logger.isDebugEnabled()) {
logger.debug(
"SecurityContextHolder not populated with remember-me token, as "
+ "AuthenticationManager rejected Authentication returned by RememberMeServices: '"
+ rememberMeAuth
+ "'; invalidating remember-me token",
authenticationException);
}
rememberMeServices.loginFail(request, response);
onUnsuccessfulAuthentication(request, response,
authenticationException);
}
}
chain.doFilter(request, response);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder not populated with remember-me token, as it already contained: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
chain.doFilter(request, response);
}
}
먼저 SecurityContextHolder 에서 인증정보가 있는지 체크하고 없으면 remember-me 인증 로직을 거친다.
rememberMeServices.autoLogin(request, response) 에서 저장된 인증정보를 불러와
SecurityContextHolder에 넣어준다.
- 터미널에서 아래와 같은 명령어로 url 을 호출해볼 수 있다.
curl -u jake:123 http://localhost:8080/dashboard
'SpringFrameWork > SpringSecurity' 카테고리의 다른 글
[SpringSecurity #13] 타임리프 스프링 시큐리티 (0) | 2020.10.05 |
---|---|
[SpringSecurity #12] Security Custom Filter 만들기 (0) | 2020.10.04 |
[SpringSecurity #10] HeaderWriterFilter, CsrfFilter (0) | 2020.10.03 |
[SpringSecurity #09] Spring Security Filter (0) | 2020.10.02 |
[SpringSecurity #08] 스프링시큐리티 아키텍처 정리 (0) | 2020.10.02 |