Spring Security ‐ 인증 프로세스 이해 - dnwls16071/Backend_Summary GitHub Wiki
📚 인증 프로세스
[폼 인증 - formLogin() & UsernamePasswordAuthenticationFilter]
- HTTP 기반의 폼 로그인 인증 메커니즘을 활성화하는 API로 사용자 인증을 위한 사용자 정의 로그인 페이지를 쉽게 구현할 수 있다.
- 기본적으로 스프링 시큐리티가 제공하는 기본 로그인 페이지를 사용하며 사용자 이름과 패스워드가 포함된 간단한 양식을 제공한다.
- 사용자는 웹 폼을 통해 자격 증명(Credentials)을 제공하고 Spring Security는
HttpServletRequest에서 값을 읽어온다.
FormLoginConfigurer설정 클래스를 통해 여러 API들을 설정할 수 있다.
- 기본적으로
UsernamePasswordAuthenticationFilter를 사용한다. - formLogin은 SPA나 API 서버에서는 잘 사용하지 않는다.
- 스프링 시큐리티는
AbstractAuthenticationProcessingFilter클래스를 사용해 사용자 자격 증명을 인증하는 기본 필터로 사용한다. UsernamePasswordAuthenticationFilter는AbstractAuthenticationProcessingFilter를 확장한 클래스로서HttpServletRequest에서 제출된 사용자 이름과 비밀번호(자격증명)로부터 인증을 수행한다.- 인증 프로세스가 초기화 될 때 로그인 페이지와 로그아웃 페이지 생성을 위한
DefaultLoginPageGeneratingFilter및DefaultLogoutPageGeneratingFilter가 초기화 된다.
[기본 인증 - HttpBasic() & BasicAuthenticationFilter]
- HTTP는 액세스 제어와 인증을 위한 프레임워크를 제공하며 가장 일반적인 인증 방식은 Basic 인증 방식이다.
- RFC 7235 표준이며, 인증 프로토콜은 HTTP 인증 헤더에 기술되어 있다.
- 클라이언트는 인증 정보 없이 서버에 접속을 시도한다.
- 서버가 클라이언트에게 인증하라는 요구를 보낼 때 401 Unauthorized 응답과 함께 WWW-Authentication 헤더를 기술해서 realm(보안 영역)과 Basic 인증 방법을 보낸다.
- 클라이언트가 서버로 접속할 떄 Base64로 username과 password를 인코딩하고 Authorization 헤더에 담아서 요청한다.
- 성공적으로 완료되면 정상적인 상태 코드를 반환한다.
❗base64 인코딩된 값은 디코딩이 가능하기 때문에 인증 정보가 노출된다. ❗HTTP basic 인증은 반드시 HTTPS와 같이 암호화 기술이 함께 사용되어야만 한다.
BasicAuthenticationFilter필터는 기본 인증 서비스를 제공하는데 사용된다.BasicAuthenticationConverter를 사용해서 요청 헤더에 기술된 인증정보 유효성을 체크하며 Base64 인코딩된 username과 password를 추출한다.- 인증 이후 세션을 사용하는 경우와 사용하지 않는 경우에 따라 처리 흐름의 차이가 존재한다.
- 세션을 사용하는 경우 서버 측의 세션 저장소에서 관리되기 때문에 인증 과정을 거치지 않으나 세션을 사용하지 않으면 매 요청마다 인증 과정을 거치게 된다.
[기억하기 인증 - RememberMe() & RememberMeAuthenticationFilter]
- 사용자가 웹 사이트나 애플리케이션에 로그인할 때 자동으로 인증 정보를 기억하는 기능이다.
UsernamePasswordAuthenticationFilter와 함께 사용되며,AbstractAuthenticationProcessingFilter슈퍼 클래스에서 훅을 통해 구현된다.- 인증 성공 시,
RememberMeServices.loginSuccess()메서드가 호출되면서 RememberMe 토큰을 생성하고 쿠키로 전달한다. - 인증 실패 시,
RememberMeServices.loginFail()메서드가 호출되면서 쿠키를 제거한다. LogoutFilter와 연계해서 로그아웃 시 쿠키를 지운다.
- 기본적으로 암호화된 토큰으로 생성되며 브라우저에 쿠키를 보내고 향후 세션에서 이 쿠키를 감지해 자동 로그인이 이루어지는 방식으로 진행된다.
- base64(username + ":" + expirationTime + ":" + algorithmName + ":" algorithmHex(username + ":" + expirationTime + ":" password + ":" + key))
- username: UserDetailsService 로 식별 가능한 사용자 이름
- password: 검색된 UserDetails 에 일치하는 비밀번호
- expirationTime: remember-me 토큰이 만료되는 날짜와 시간, 밀리초로 표현
- key: remember-me 토큰의 수정을 방지하기 위한 개인 키
- algorithmName: remember-me 토큰 서명을 생성하고 검증하는 데 사용되는 알고리즘(기본적으로 SHA-256 알고리즘을 사용)
TokenBasedRememberMeServices: 쿠키 기반 토큰 보안을 위해 해싱이 사용된다.PersistentTokenBasedRememberServices: 생성된 토큰을 저장하기 위해 데이터베이스나 다른 영구 저장 매체를 사용한다.- 두 구현 모두 사용자 정보를 검색하기 위해
UserDetailsService인터페이스를 구현해야한다.
RememberMeAuthenticationFilter는SecurityContextHolder에 Authentication이 포함되지 않은 경우 실행되는 필터이다.- 세션이 만료되었거나 애플리케이션 종료로 인해 상태가 소멸된 경우 토큰 기반 인증을 사용해 유효성을 검사하고 토큰이 검증되면 자동 로그인 처리를 수행한다.
[익명 인증 사용자 - Anonymous() & AnonymousAuthenticationFilter]
- 스프링 시큐리티에서 익명으로 인증된 사용자와 인증되지 않은 사용자 간에 실제 개념적 차이는 없으며 단지 액세스 제어 속성을 구성하는 편리한 방법을 제공한다.
SecurityContextHolder가 항상Authentication객체를 포함하고 null을 포함하지 않는다는 것을 규칙을 세우게 되면 클래스를 더 견고하게 작성할 수 있다.- 인증 사용자와 익명 사용자를 구분해서 어떤 기능을 수행하고자 할 때 유용하게 사용 가능하며 익명 인증 객체를 세션에 저장하지 않는다.
- 익명 인증 사용자의 권한을 별도로 운용할 수 있다. 인증된 사용자가 접근할 수 없도록 구성한다.
- 만약 익명 요청에서
Authentication을 얻고 싶다면@CurrentSecurityContext를 사용하면 된다. - CurrentSecurityContextResolver에서 요청을 가로채어 처리한다.
[로그아웃 - logout() & LogoutFilter]
- 스프링 시큐리티는 기본적으로
DefaultLogoutPageGeneratingFilter를 통해 로그아웃 페이지를 제공해GET /logoutURL로 접근이 가능하다. - 로그아웃 실행은 기본적으로
POST /logout으로도 가능하나 CSRF 기능을 비활성화 혹은RequestMatcher를 사용하면 GET, PUT, DELETE 모두 가능하다. - 로그아웃 필터를 거치지 않고 스프링 MVC에서 커스텀 구현도 가능하며 로그인 페이지가 커스텀하게 생성될 경우 로그아웃 기능 역시 커스텀으로 구현해야 한다.
[요청 캐시 - RequestCache / SavedRequest & RequestCacheAwareFilter]
RequstCache는 인증 절차 문제로 리다이렉트 된 후에 이전에 했던 요청 정보를 담고 있던SavedRequest객체를 쿠키 혹은 세션에 저장하고 필요시 다시 가져와 실행하는 캐시 메커니즘이다.SavedRequest는 로그인과 같은 인증 절차 진행 후 사용자를 인증 이전의 마지막 페이지로 안내하며 이전 요청과 관련된 여러 정보를 저장한다.
❗그러나 만약 인증되지 않은 사용자가 매번 요청을 보내면 위와 같은 흐름이 계속 진행될텐데 불필요한 과정이 될 수가 있다. 그래서 요청을 저장하지 않도록 하려면
NullRequestCache구현을 사용할 수 있다.
RequestCacheAwareFilter는 이전에 저장했던 웹 요청을 다시 불러오는 역할을 한다.SavedRequest가 현재 Request와 일치하면 이 요청을 필터 체인의doFilter메서드에 전달하고SavedRequest가 없으면 필터는 원래 Request를 그대로 진행시킨다.