🙏내용에 대한 피드백은 언제나 환영입니다!!🙏
지금 사용하는 벨로그도 그렇듯 어느 홈페이지나 로그인은 필요하다.
그래서, 이번 글에서는 Spring Security에 대해서 알아 보려고 한다.
Spring Security는 애플리케이션의 보안을 담당하는 스프링 하위 프레임워크이다.
여기서 보안은 '인증'과 '인가'로 나뉘게 된다.
인증(Authentication)이란, 사용자가 자신이 주장하는 신원을 증명하는 것. 즉 '로그인 과정'이다.
인가(Authorization)란, 인증된 사용자가 특정 자원에 접근할 수 있는 권한을 부여하는 것. 즉, '접근 권한'이다.
Spring Security Config를 등록하게 되면 Filter에 등록이 되어 인증과 인가에 대한 역할을 수행한다.
클라이언트가 요청을 보내면
HTTP 요청 -> WAS(Web Application Serve) -> Filter -> Servlet -> Interceptor -> Controller 순으로 진행된다.
위 그림은 인증 아키텍쳐이다.
아키텍쳐의 과정을 보면
HTTP 요청(로그인)이 들어오면 Spring Security Config로 인해 등록된 AuthenticationFilter가 요청을 가로채고
로그인 정보를 통해 UsernamePasswordAuthenticationToken 객체로 변환된다.
(UsernamePasswordAuthenticationToken class를 들어가보면, 유저의 ID가 Principal의 역할을 하고 유저의 Password가 Credential의 역할을 한다.)
AuthenticationFilter에서 UsernamePasswordAuthenticationToken을 ProviderManger(AuthenticationManager의 구현체)에 인증을 요청하기 위해 보낸다.
그리고, AuthenticationManager는 인증 요청을 위해 등록된 AuthenticationProvider(s)에 보낸다.
(사용자 이름과 비밀번호 기반 인증 처리하는 DaoAuthenticationProvider, OAuth2.0 로그인은 OAuth2LoginAuthenticationProvider)
AuthenticationProvider(s)는 UserDetailsService를 이용하여 사용자 정보를데이터 베이스에서 가져온다.
받은 사용자 정보를 통해 데이터베이스에서 찾아낸 정보인 UserDetails 객체를 만든다.
AuthenticationProvider(s)는 User 객체의 정보를 UserDetails로 부터 받아 사용자 정보를 토큰 정보와 비교한다.
인증이 성공한다면, AuthenticationProvider(s)는 Authentication 객체를 AuthenticationManager에 반환한다.
(실패하면, AuthenticationException을 throw한다.)
AuthenticationManager는 SecurityContext에 인증된 사용자 정보를 설정한다. 이는 현재 사용자의 인증 정보를 유지하는 데 사용된다.
SecurityContextHolder는 현재 실행 중인 스레드에 SecurityContext를 설정한다. 이를 통해 현재 스레드에서 사용자의 인증 정보에 쉽게 접근할 수 있다.
사용자가 특정 리소스에 접근하려고 할 때, SecurityContextHolder는 현재 스레드의 SecurityContext에서 Authentication 객체를 가져와 사용자의 권한을 확인인한다. 이 Authentication 객체에는 사용자의 인증 정보와 권한이 포함되어 있으므로, 이를 통해 해당 사용자가 특정 작업을 수행할 수 있는지 확인할 수 있다.
Authentication 객체에는 현재 접근하는 주체의 정보와 권한이 담겨있다.
SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 이 구문은 사용자 정보를 가지고 올 수 있다.
추가로, SecurityContextHolder.getContext().getAuthentication().getName()은 사용자 아이디를 가져올 수 있다.
OAuth2AuthorizationRequestRedirectFilter → OAuth2LoginAuthenticationFilter → OAuth2LoginAuthenticationProvider -> 이후 위 'Spring Security 아키텍쳐'와 같이 OAuth2UserSercice 실행하여 OAuth2User 객체를 만들어 세션 저장과 같은 나머지 Security 로직 동작.
이해를 돕기 위해 먼저 동작 방식을 보겠다.
네이버로 로그인 한다고 가정해보자.
OAuth 2.0을 사용하여 네이버와 같은 서비스에 로그인하는 경우 일반적으로 다음과 같은 절차를 따른다.
사용자가 네이버 로그인 버튼을 클릭.
애플리케이션은 네이버의 OAuth 2.0 인증 서버에 사용자를 리디렉션하여 로그인을 요청.
사용자는 네이버의 로그인 페이지에서 자신의 계정 정보를 입력.
네이버는 사용자가 로그인을 허용할 것인지 묻는 권한 동의 페이지를 표시.
사용자가 권한을 허용하면, 네이버는 애플리케이션에게 인증 코드(authorization code)를 제공.
애플리케이션은 받은 인증 코드를 사용하여 네이버에게 액세스 토큰(access token)을 요청.
네이버는 애플리케이션에게 액세스 토큰을 발급.
애플리케이션은 받은 액세스 토큰을 사용하여 사용자의 정보를 요청하거나 보호된 리소스에 접근.
받은 사용자 정보를 이용하여 로그인 상태를 유지하기 위해 세션 사용.
위와 같은 동작 방식이 있다.
8번 부터 위 'Spring Security 아키텍쳐'와 같이 OAuth2UserSercice 실행하여 OAuth2User 객체를 만들어 세션 저장과 같은 나머지 Security 로직 동작.
스프링 시큐리티에 대해 공부를 하였지만, 이 글으 작성하기 전까진 흐름에 대한 이해도는 부족했다. 하지만, 글을 정리하며 사용자 정보를 얻어 특정 Entity 생성, 삭제 등을 할 때 활용했던 SecurityContextHolder.getContext().getAuthentication().getName()이 어떻게 나오게 되었는지 알게 되었다.
어느 기술에서든 기본이 중요하다. 기본을 알아야 응용이 가능하고, 어떠한 문제가 발생하면, 어디서 발생한지 알게 된다.
다음 글은, Spring Security를 적용해보겠다.