Spring Security 로그인 인증절차
- -
이전 포스팅에서는 해당 과정을 설정하지 않고 기본 Spring Security 설정에 따라 로그인을 구현했었다.
아래 내용 확인 부탁드립니다. 아래 내용은 WebSecurityConfigurerAdapter가 Deprecated 되어 있는 상태에서 설정했다.
https://okimaru.tistory.com/293
AbstractAuthenticationProcessingFilter
Spring Security 에서 로그인을 할때 실질적으로 받는 설정이 loginProcessingUrl("URL") 이다.
사용자가 로그인화면에서 아이디, 패스워드를 입력했을 때 체크하는 Filter 가
AbstractAuthenticationProcessingFilter 이며, AbstractAuthenticationProcessingFilter 를 상속하여 확장해놓은 UsernamePasswordAuthenticationFilter 클래스가 있다.
doFilter()
attemptAuthentication()
AbstractAuthenticationProcessingFilter 에는 doFilter() 메서드와 attemptAuthentication() 이라는
추상메서드가 존재한다.
doFilter() 메서드는 FilterChainProxy 에서 Filter 목록들을 호출할 때 사용하는 메서드이다.
그리고 doFilter() 중간에는 attemptAuthentication() 이라는 추상메서드를 호출한다.
위는 AbstractAuthenticationProcessingFilter 의 attemptAuthentication() 추상메서드이고
아래는 AbstractAuthenticationProcessingFilter 를 상속받은
UsernamePasswordAuthenticationFilter 클래스에서 재정의한 attemptAuthentication() 메서드이다.
UserPasswordAuthenticationToken
attemptAuthentication 메서드에서 UsernamePasswordAuthenticationToken 과
this.getAuthenticationManager() 를 확인하자
UsernamePasswordAuthenticationToken 은 AbstractAuthenticationToken 을 상속받고
AbstractAuthenticationToken 은 Authentication 인터페이스를 구현한다.
즉, UsernamePasswordAuthenticationToken 은 사용자 인증이 완료되면 SecurityContextHolder에
인증객체인 Authentication 으로 저장된다.
다시 UsernamePasswordAuthenticationToken 클래스로 돌아와 생성자 함수 2개를 보자
UsernamePasswordAuthenticationToken 의 생성자 함수 2개
위 생성자 함수 2개의 차이점은 authorities 의 여부와 setAuthenticated(false),setAuthenticated(true) 의 차이다.
authorities 의 존재 여부에 따라 없을 경우 아직 인증되지 않은 AuthenticationToken 이며
authorities 가 존재하는 생성자함수를 호출하게 되면 인증된 생성자로 Authentication 객체가 생성되는것이다.
로그인 인증을 하는 UsernamePasswordAuthenticationFilter 의 attemptAuthentication() 코드를 다시 보자
setAuthenticated(false) 가 존재하는 생성자 함수를 호출하는것을 볼 수 있다.
아직 인증되지 않은 Authentication 을 생성한것이고 인증이 완료되었을 때 인증된 Authentication 객체가 생성된다.
두번째는 this.getAuthenticationManager() 를 확인하자
해당 this.getAuthenticationManager() 로 가보면 AbstractAuthenticationProcessingFilter 의
AuthenticationManager 가 있으며, 해당 AuthenticationManager 객체를 가져오는것이다.
AuthenticationManager
AuthenticationManager 는 AuthenticationProvider 라는 클래스 객체를 관리한다.
AuthenticationManager 는 인터페이스이며 authenticate() 라는 메서드만 정의되어 있다.
return 타입은 Authentication 이며 authenticate() 메서드를 실행하게 되면 인증된 Authentication 객체를
반환하는 기능을 하는 메서드이다.
그리고 UsernamePasswordAuthenticationFilter 의 AuthenticationManager 를 구현하는
구현체는 아래 이미지처럼 ProviderManager 가 구현하고 있다.
그리고 ProviderManager 는 AuthenticationManager 인터페이스의 authenticate() 메서드를 override 한다.
AuthenticationManager 는 AuthenticationProvider 를 관리한다고 말했던것처럼 위 for문에서
AuthenticationProvider 객체들을 반복하여 로직이 진행되는걸 확인할 수 있다.
AuthenticationProvider
이제 실제 인증로직이 진행될 AuthenticationProvider 를 봐보자
AuthenticationProvider 의 authenticate() 메서드는 실제 인증 로직이 담길 메서드이고
supports() 는 매개변수로 받은 authentication 객체의 구현체 클래스와 AuthenticationProvider 에서
사용하는 Authentication 객체와 같은지 확인한다.
아래의 코드를 보면 ProviderManager 에서 모든 AuthenticationProvider 를 반복해서 supports 메서드를
실행하는걸 볼 수 있다.
Authentication 객체는 AuthenticationProvider 마다 다르기 때문에 아래 반복문 로직이
Authentication 객체에 알맞는 AuthenticationProvider 를 찾는 과정이다.
이제 Spring Security 에 기본적으로 설정된 AuthenticationProvider 의 구현체를 봐보자
기본적으로 설정된 구현체는 AbstractUserDetailsAuthenticationProvider 이다.
AbstractUserDetailsAuthenticationProvider
AuthenticationProvider 를 구현한 AbstractUserDetailsAuthenticationProvider 클래스의
authenticate() 메서드이다.
authenticate() 구현 메서드에서 확인해야할건 UserDetails 객체를 가지고 오는 부분이다.
retrieveUser 추상메서드는 사용자 ID와 사용자가 입력한 로그인정보가
저장되는 authenticate 객체를 전달해 UserDetails 객체를 불러온다.
UserDetails 객체를 어떻게 불러오는건지 확인해보자
AbstractUserDetailsAuthenticationProvider 도 추상클래스이므로 또 다른 클래스가 상속받아서 사용한다.
AbstractUserDetailsAuthenticationProvider 를 상속받은 클래스는 DaoAuthenticationProvider 이다.
DaoAuthenticationProvider 의 재정의 메서드 중 중요한건 2개의 메서드이다.
additionalAuthenticationChecks() 메서드와 UserDetails 객체를 반환하는 retrieveUser() 메서드이다.
additionalAuthenticationChecks() 메서드는 사용자가 입력한 로그인 정보인 authentication 객체와
사용자 ID를 통해 DB에서 불러온 사용자정보가 저장된 userDetails 객체를 비교해 인증하는 메서드이다.
그리고 retrieveUser() 메서드는 getUserDetailsService() 객체의 loadUserByUsername 메서드를 통해
사용자 ID의 DB 정보를 불러와 UserDetails 객체를 리턴한다.
위 두개의 메서드를 통해 사용자가 입력한 로그인 정보와 DB 에서 불러온 사용자 정보를 비교해 인증이
완료 된다면 AbstractUserDetailsAuthenticationProvider() 의 authenticate() 메서드 return 부분에
createSuccessAuthentication() 메서드를 통해 인증완료된 Authentication 이 리턴된다.
위 createSuccessAuthentication 메서드는 UsernamePasswordAuthenticationToken 을 생성하여 리턴한다.
UsernamePasswordAuthenticationToken 의 생성자함수는 2개였다.
하나는 인증되지 않은 Authentication을 리턴하고 또 다른 하나는 인증된 Authentication을 리턴한다.
해당 createSuccessAuthentication 에서는 인증된 Authentication 객체를 리턴하는걸 볼 수 있다.
이제 인증된 Authentication 객체는 비교할 로그인 DB 정보가 저장되는 UserDetails 데이터를 UsernamePasswordAuthenticationFilter 클래스의
attemptAuthentication() 메서드의 통해 리턴된다.
이러한 attemptAuthentication() 메서드를 호출하는건 AbstractAuthenticationProcessingFilter 추상 클래스의
doFilter() 에서 호출한다.
authenticationResult 에 인증된 Authentication 객체가 저장되고 successfulAuthentication() 메서드가 실행된다.
successfulAuthentication 메서드에서 인증된 Authentication 객체를 SecurityContext 에 저장하고
인증 프로세스가 종료된다.
아래 이미지는 위의 내용을 토대로 인증을 하는 architecture 이다.
요약
1. Spring Security Filter 중 AbstractAuthenticationProcessingFilter 상속한 UsernamePasswordAuthenticationFilter
의해 인증 프로세스가 진행된다.
2. UsernamePasswordAuthenticationFilter 클래스의 attemptAuthentication() 메서드를 통해 인증되지 않은
Authentication 객체를 생성한다.(UsernamePasswordAuthenticationToken 생성)
3. attemptAuthentication() 에서 인증되지 않은 Authentication 객체를 AuthenticationManager 클래스를
호출하면서 전달한다.
4. AuthenticationManager 는 인터페이스이고 AuthenticationManager 를 구현한 구현체는 ProviderManager이다.
5. ProviderManager 는 AuthenticationProvider 를 통해 사용자 인증정보인 Authentication을 넘기면서
로그인 인증을 시도한다.
6. AuthenticationProvider 는 넘겨받은 사용자 로그인정보 Authentication과 UserDetailsService 를 통해 불러온
사용자 DB 정보를 담은 UserDetails 객체를 비교하게 된다.
7. 입력한 로그인정보(Authentication)와 UserDetails 객체(사용자 DB 정보)가 동일하면 인증한 Authentication 객체가
생성된다.
8. 인증된 Authentication 객체를 SecurityContextHolder에 담게되고 인증 프로세스가 종료된다.
아래 너무 친절하게 알려주신 awdsd 님껄 보고 작성한 글입니다.
참고한 사이트
https://cjw-awdsd.tistory.com/45
'Java > Spring' 카테고리의 다른 글
외부 .properties, .yml 파일 주입해서 사용하기(@Value) (0) | 2022.11.11 |
---|---|
추상클래스와 인터페이스(abstract class, interface) (0) | 2022.11.10 |
[Spring Boot] Spring Security WebSecurityConfigurerAdapter Deprecated 로 인해 Security 설정 바꿔보기 (0) | 2022.11.04 |
[Spring Boot] Ajax 요청시 302 Redirect (302 에러) (0) | 2022.11.01 |
[Spring Boot] Spring Security를 이용한 로그인 설정 (0) | 2022.10.31 |
소중한 공감 감사합니다