
웹 플랫폼의 회원가입은 JWT 와 Spring Security 를 적용하여 구현하였다. 지난 게시글에는 JWT 를 알아봤다면, 이번엔 스프링 시큐리티에 대해 설명하고자 한다. 스프링 시큐리티는 내용이 너무 많고 깊기 때문에 추후 공식 문서를 따로 정리하도록 하고, 지금은 로그인/회원가입에 적용한 시큐리티 핵심만 알아보자.
스프링 시큐리티는 인증/인가를 지원하고 주요 공격으로부터 어플리케이션을 보호해주는 프레임워크이다.
사실상 스프링 기반 어플리케이션의 표준 보안 프레임워크이다. 스프링 시큐리티를 실행하려면 자바8 이상의 환경이 필요하다. 회원가입, 로그인/로그아웃, 권한 관리를 위한 인증과 인가의 보안 기능을 제공한다.
가장 큰 이유는 스프링 보안에 필요한 기능을 제공하기 때문이다.
스프링 시큐리티는 개발 구조가 스프링이라는 프레임워크 안에서 활용하기 적합한 구조로 설계되어 있어 보안 기능을 추가할 때 활용하기 좋다.
기존에 아키텍처를 찾아보면 엄청 복잡해서 머리가 아프다 .. 정말 어렵다 ..
그래서 완전 '핵심만' 간추려 도식화하면 아래와 같다.
(※ 이 그림은 본인이 이해하기 쉽게 도식화한 거라 틀릴 수도 있다.)

사용자(Client) 가 로그인을 시도한다.
AuthenticationFilter 가 사용자의 인증 정보(ex. ID/PW) 를 받아 Authenticaiton객체를 생성한다.
생성된 Authentication 객체는 AuthenticationManager 에게 전달된다.
AuthenticationProvider 가 UserDeatilsService 를 호출하여 사용자 상세 정보를 불러온다. AuthenticationProvider 는 loadUserByUsername 메소드를 통해 UserDetailsService 에 사용자명(username) 을 전달하고, UserDetailsService 는 해당 사용자명에 해당하는 UserDetails 객체를 반환한다.
UserDetails 객체는 Provider 에서 인증을 수행한다.
인증이 성공하면,ProviderManager 에 권한을 담은 토큰을 전달한다.
(※ ProviderManager : AuthenticationProvider의 실제 구현체)
ProviderManager 는 AuthenticationFilter 를 거쳐SecurityContextHolder 의 SecurityContext 에 저장된다.
이후 시스템은 사용자가 인증된 상태임을 인증하고, 사용자의 권한에 따라 접근을 허용한다.
📍 주의할 점은, 최초 Authentication 과 최종 Authentication 의 상태가 다르다는 점이다!
AuthenticationFilter 는 UsernamePasswordAuthenticationFilter 등을 사용해 인증 요청을 가로채서 사용자의 인증 정보를 수집하고, 이를 Authentication 객체로 매핑하여 AuthenticationManager 에게 전달한다. 그러면, AuthenticationManager 가 AuthenticationProvider 를 통해 실제 인증을 수행한다. 따라서 AuthenticationProvider 는 주로 비밀번호 확인 또는 외부 시스템과의 인증을 처리하는 로직이 구현된다. 여기서 실제 인증된 Authentication 객체를 생성한다.
👉 정리하면, Authentication 은 Filter 와 Provider 에서 생성되는 데 이 둘의 차이는 인증 유무에 따라 나눌 수 있다.
AuthentcationFilter 가 된다. 이때 Authentication 은 인증되지 않는 상태로 생성된다. Authentication 은 AuthenticationProvider 에서 인증이 완료된 후 생성된다. SecurityContext 에 저장된Authentication 객체로부터 사용자의 인증 정보를 조회한다.GranedAuthority)을 확인하여 해당 URL 에 접근 가능한지 결정한다.📍 인가 과정에서는 결국 권한이 있느냐가 가장 중요하다.
스프링 시큐리티는 이를 확인하기 위한 여러 매커니즘을 제공하는데, 가장 흔히 사용되는 방법 중 하나는@PreAuthorize, @PostAuthorize, @Secured 등의 어노테이션을 메서드에 적용하는 것이다. 이 어노테이션들을 통해 특정 메서드에 대한 접근을 특정 권한을 가진 사용자로 제한할 수 있다.
// ex) USER 권한으로 제한
@PreAuthorize("hasRole('USER')")
public void create(Contact contact);
ROLE_USER 권한이 있는 사용자만 접근할 수 있다는 뜻이다. 여기서 ROLE_USER 의 ROLE_ 은 스프링 시큐리티에서 prefix 되어 있기 때문에 권한명은 USER 만 작성한다.※ SpringBoot 2.6.0 에서 진행
// Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'