이전에는 스프링 시큐리티 아키텍쳐에 따라 어떤식으로 동작이 되는지 보았다.

 

 

[Spring] 스프링 시큐리티(Spring Security) + JWT 토큰 구현(1)

스프링 시큐리티는 스프링 기반 애플리케이션의 인증, 보안 등을 처리하기 사용되는 스프링 하위 프레임워크로  권한을 부여하여 접근을 제한하고 로그인 유지 등의 용도로 사용이 된다. 스프

treecode.tistory.com

 

스프링 시큐리티를 사용하기 위해서는 Security Configuration 설정 클래스를 만들어야 되는데 이전에는 WebSecurityConfigureAdapter를 extends해서 configure 메소드를 오버라이드 했지만 최근에 스프링 버전이 바뀌면서 WebSecurityConfigureAdapter가 Deprecated 되었다. 그래서 SecurityFilterChain을 Bean으로 등록해서 설정해야 한다.

 

1번을 보면  @EnableWebSecurity 애노테이션은 스프링 시큐리티를 사용하기 위해 필수로 적용하는 애노테이션이다.

filterChain 파라미터를 보면 HttpSecurity를 받을 수 있는 것도 @EnableWebSecurity에 HttpSecurityConfiguration이 Import되어 있기 때문에 가능하다.  

@EnableWebSecurity에 Import 되어 있는 WebSecurityConfiguration을 보면 springSecurityFilterChain() 메소드를 실행해서 Beand으로 등록한 WebSecurityCustomizer, SecurityFilterChain 등으로 WebSecurity를 생성한다.

 

WebSecurity는 performBuild() 메소드를 통해 FilterChainProxy를 만드는데 이 FilterChainProxy는 DelegatiingFilterProxy로부터 위임을 받아 실제 처리를 담당하는 스프링 시큐리티의 필터의 핵심 클래스이다.

 

DelegatingFilterProxy서블릿 컨테이너와 스프링 컨테이너 사이를 연결하는 서블릿 필터로  Http Request 요청이 오면 FilterChainProxy로 위임(Delegate)을 해서 스프링 시큐리티의 기본 필터와 커스텀 필터를 거치게 된다.

 

아래 그림을 보면 Request가 서블릿에 도달하기 전 SecurityFilterChain이 적용되는 것을 볼 수 있는데 서블릿 필터에서 스프링 빈 필터를 주입 받아 사용할 수 없기 때문에 중간 다리 역할을 해주는 스프링 시큐리티의 특수한 필터인 FilterChainProxy에게 위임을 하는 것이다.

 

 

 

 

 

 

@EnableGlobalMethodSecurity는 컨트롤러에 @PreAuthorize 애노테이션을 사용하여 권한에 따라 접근할 수 있도록 하기 위해 선언한다.

 

 

2번에서는 예외를 핸들링하기 위해  AccessDeniedHandler, AuthenticationEntryPoint를 추가하였고 커스텀 AuthenticationProvider를 주입하였다. 커스텀 AuthenticationProvider는 따로 코드로 자세히 보겠지만 Jwt 토큰을 인증하고 발급하는 등의 처리를 담당한다. 4번, 7번에서 각각 적용하는 것을 볼 수 있다.

 

 마지막으로 3번, 6번을 보면 특정 리소스를 무시하고 어떠한 요청에 인가(authorization)을 해주는지에 대한 설정을 하는 것이고 8번의 PasswordEncoder는 비밀번호를 암호화하기 위해 BcryptPasswordEncoder를 빈으로 등록하였다.

 

다음에는 JWT 토큰에 대해 자제시 알아보고 7번에서 JwtFilter에  jwtAuthenticationProvider를 주입해서 필터로 등록하는 것에 대해 공부한다. 

 

 

[참고]

https://oh-sh-2134.tistory.com/114

https://uchupura.tistory.com/24

https://velog.io/@jeongyunsung/스프링부트-해부학-Security1-

스프링 시큐리티스프링 기반 애플리케이션의 인증, 보안 등을 처리하기 사용되는 스프링 하위 프레임워크로  권한을 부여하여 접근을 제한하고 로그인 유지 등의 용도로 사용이 된다.

 

 

스프링 시큐리티의 기본 동작은 컨트롤러를 통해 유저 정보를 담은 Http Request 요청이 오면 AuthenticationFilter에서 인증 전 상태의 UsernamePasswordAutienticationToken을 생성한다.

UsernamePasswordAutienticationToken은 Authentication 인터페이스를 implements한 AbstractAuthenticationToken을 상속 받은 클래스로 두개의 생성자를 보면 인증 전, 인증 후 토큰을 생성하는 것을 알 수 있다. (principal : 아이디, credentials : 비밀번호)

 

 

해당 토큰으로 AuthenticationManagerBuilder를 통해 AuthenticationManager에서 authenticate() 메서드를 실행하면 내부적으로 AuthenticationManager의 구현체인 ProviderManager가 동작한다.

 

ProviderManager는 DaoAuthenticationProvider를 호출한다. (DaoAuthenticationProvider는 AuthenticationProvider의 추상 클래스인 AbstractUserDetailsAuthenticationProvider를 상속 받음)

DaoAuthenticationProvider의 retrieveUser() 메소드를 보면  UserDetailsService의 loadByUsername() 메서드를 실행하는 것을 볼 수 있다.

 

 

UserDetailsService는 커스텀으로 구현하는데 loadUserByUsername을 오버라이드해서 파라미터로 넘어온 Username으로 조회를 하고 존재할 경우 UserDetails 객체의 인스턴스를 생성해서 반환한다.

유저 정보가 인증이 되었으면 UserDetails 인스턴스가 반환이 되고 다시 DaoAuthenticationProvider로 돌아와서 비밀번호(Credential)가 일치하는지 확인을 한다. 인증 전 로그인 정보의 비밀번호는 암호화가 되어있지 않지만 내부적으로 passwordEncoder가 작동한다.

 

정리를 하자면 ProviderManager에서 authenticate()가 실행이 되면 DaoAuthenticationProvider에서 실질적인 인증 처리를 한다.

 

1) 유저 정보를 확인하기 위해 retrieveUser()가 실행, UserDetailsService의 loadUserByUsername()를 통해 UserDetails 반환

2) 비밀번호를 확인하기 위해 additionalAuthenticationChecks()가 실행, 반환 받은 UserDetails 정보로 비밀번호 검증

 

이렇게 인증이 완료된 Authentication 인스턴스를 SecurityContext에 저장을 하고 SecurityContextHolder에 넣는다.

스프링 시큐리티를 세션 방식으로 구현할 때 SecurityContext에 인증이 완료된 Authentication 인스턴스를 저장하고 이를SecurityContextHolder에 담아 보관을 하는데 JWT 토큰으로 구현을 하더라도 똑같이 저장을 해야 한다.

SecurityContext에 Authentication 객체를 저장하는 이유는 세션과 상관 없이 ThreadLocal 때문이다. SecurityContextHolder는 ThreadLocal을 가지고 있는데 ThreadLocal은 세션의 범위가 아닌 Request 범위에 속한다. SecurityContext에 Authentication 객체가 저장되어 있지 않으면 FilterSecurityIntercepter에서 필터에 걸리게 된다.

(SecurityContextHolder와 ThreadLocal에 대한 부분은 좀 더 공부를 해봐야 알 것 같다.)

https://www.inflearn.com/questions/501092

https://www.inflearn.com/questions/558844

 

 

[참고]

 

https://bcp0109.tistory.com/301

https://mangkyu.tistory.com/76

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-jwt

 

 

 

스프링 시큐리티(Spring Security)는 스프링 기반의 사용자 인증을 처리하는 스프링 하위 프레임워크로 사용자 인증과 권한에 대한 부분을 Filter로 처리해준다.

 

 

핵심 용어


SecurityContextHolder

 

보안 주체의 세부 정보를 포함하여 응용 프로그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장되며 로그인 인증이 되면 스프링 시큐리티는 SecurityContextHolder에 Authentication을 담아 세션 저장소에 있는 전용 공간에 보관한다.

 

SecurityContext

 

Authentication 객체를 보관하는 역할을 하며 SequrityContext를 통해 Authentication 객체를 꺼낼 수 있다.

 

Authentication

 

현재 접근하는 주체의 정보와 권한을 담는 인터페이스로 Authentication 객체는 SecurityContext에 저장되며, SecurityContextHolder를 통해 SecurityContext에 접근해서 Authentication 객체를 가져올 수 있다.

 

UsernamePasswordAuthenticationToken

 

Authentication을 implements한 AbstractAuthenticationToken의 하위 클래스로 사용자 인증 전 객체를 생성하는 생성자가 있고, 인증이 완료된 객체를 생성하는 생성자가 있다.

 

AuthenticationManager, AuthenticationProvider

 

인증을 처리하는 방법을 정의한 인터페이스로 구현체인 ProviderManager가 있다. ProviderManager는 실제 인증을 처리하는AuthenticaitonProvider 리스트를 실행시켜 인증을 거치고 직접 Provider를 구현하여 등록할 수도 있다. 사용자 입력 정보가 있는 인증 전 객체인 Authentication으로 authenticate() 메서드를 실행하여 인증을 완료하고 하면 인증이 완료된 Authentication을 반환한다.

 

UserDetailsService, UserDetails

 

UserDetailsService는 loadUserByUsername() 메서드를 통해 DB에서 데이터 조회를 하는 역할을 하며 UserDetailsService를 implements한 클래스를 스프링 빈으로 등록하면 스프링이 자동으로 실행된다. UserDetailsService는 DB에서 조회한 User 객체를 담은 UserDetails 객체를 생성하여 반환한다.

UserDetails는 사용자 정보를 담고 있는 인터페이스로 계정이 가지고 있는 권한 리스트 등의 메서드를 Override 할 수 있으며 로그인 요청 정보와 비교하여 인증 완료된 Authentication 객체를 생성하는데 사용된다.

 

 

 

인증 과정


1. 사용자가 아이디, 비밀번호로 로그인 요청을 하면 AuthenticationFilter에서 인증 전 상태의 Authentication 객체(UserPasswordAuthenticationToken)을 생성하여 AuthenticationManager에 전달한다.

 

2. AuthenticationManager는 AuthenticationProvier를 실행하며 사용자 인증을 처리한다.

 

3. AuthenticationProvider는 UserDetailsService를 통해 인증을 처리한다.

 

4. UserDetailsService는 loadUserByUsername() 메서드를 실행하여 DB에서 데이터를 조회하고 UserDetails 객체를 생성하여 반환한다.

 

5. AuthenticationProvider는 UserDetails 객체를 받아서 비밀번호 비교 등 추가 인증을 한다. 

 

6. AuthenticationManager는 인증이 완료된 Authentication 객체 (UsernamePasswordAuthenticationToken)를 AuthenticationFilter, LoginSuccessHandler로 전달해서 SecurityContextHolder에 저장한다.

 

7. SecurityContextHolder는 세션 저장소에 있는 스프링 시큐리티 전용 공간에 저장이 된다.

 

 

 

 

 

 

[참고]

 

[SpringBoot] Spring Security란?

대부분의 시스템에서는 회원의 관리를 하고 있고, 그에 따른 인증(Authentication)과 인가(Authorization)에 대한 처리를 해주어야 한다. Spring에서는 Spring Security라는 별도의 프레임워크에서 관련된 기능

mangkyu.tistory.com

 

 

[무료] 스프링부트 시큐리티 & JWT 강의 - 인프런 | 강의

스프링부트 시큐리티에 대한 개념이 잡힙니다., - 강의 소개 | 인프런...

www.inflearn.com

 

+ Recent posts