Spring Security 내부 흐름

mainimg.jpg

플로우 다이어그램

a1.png

  1. 사용자가 웹 어플리케이션에 자격증명 정보를 입력한다. 이 정보는 Spring Security Filters(Spring Security Filter Chain) 으로 넘겨진다. Spring Security Filters는 사용자가 입력한 자격증명을 가로채고, 접근 권한이 있는지 확인한다.

    Spring Security Filter Chain?

    FIlter Chain에는
    UsernamePasswordAuthenticationFilter: 사용자 이름과 비밀번호를 통한 인증을 처리
    BasicAuthenticationFilter: HTTP Basic 인증을 처리
    CsrfFilter: CSRF 공격을 방지
    LogoutFilter: 로그아웃 요청을 처리
    와 같은 필터들이 있을 수 있다.

  2. Spring Security Filters는 엔드유저가 보낸 자격증명 정보에서 유저네임과 비밀번호를 추출하여 Authentication 객체로 변환한다. Security Context에 이미 Authentication 정보가 존재하는 경우는 이후 해당 객체를 사용하고, 과정을 스킵한다.

    Authentication?

    Authentication 객체는 스프링 시큐리티 프레임워크 안에서 엔드유저의 정보를 저장하기 위한 핵심 표준이다.
    2단계에서는 유저네임와 자격증명만을 보유한다. 바로 다음과 같은 것들이다.
    principal: 사용자 이름 또는 사용자 식별자
    credentials: 사용자가 입력한 비밀번호
    이후의 인증 과정에서 이 Authentication 객체에는 더 많은 책임과 역할이 부여될 수 있다.

  3. Spring Security Filter는 생성된 초기 Authentication 객체를 Authentication Manager로 전달한다.

    Authentication Manager?

    Authentication Manager는 여러 Authentication Provider들과 협력하여 인증 과정을 수행한다:

    1. Athentication Provider에 위임: Authentication Manager는 실제 인증 작업을 여러 Authentication Provider에 위임한다. 각 Authentication Provider는 특정 인증 메커니즘을 담당한다.

    2. 인증 결과 반환: Authentication Provider에서 인증이 성공하면, Authentication Manager는 인증된 Authentication 객체를 반환한다. 이 객체는 사용자에 대한 추가 정보(권한, 인증 여부 등)를 포함한다.

    모든 Filter가 Authentication Manager에게 Authentication 객체를 넘겨주는 것은 아니다. 인증 작업을 담당하는 UsernamePasswordAuthenticationFilterBasicAuthenticationFilter에 의해 호출된다. 다른 필터들은 Authentication 객체를 만들지도 않는다.

  4. Authentication ProviderAuthentication객체를 받아서 적절한 인증 절차를 수행한다. 인증이 성공하면 Authentication객체를 반환한다. 다음은 그 예시이다.

    • DaoAuthenticationProvider: 데이터베이스에서 사용자 정보를 조회하여 인증을 수행한다.
    • LdapAuthenticationProvider: LDAP 서버를 통해 인증을 수행한다.
    • JwtAuthenticationProvider: JWT 토큰을 사용하여 인증을 수행한다.
    Provider가 반환하는 Authentication

    Authentication Provider가 인증 완료 후 반환하는 Authentication 객체에는 추가 정보가 포함된다. Principal, Credentials, Authorities, Details, Authenticated와 같은 정보가 포함된다. 추후 UserDetialsService에서 반환된 정보를 통해 ProviderUsernamePasswordAuthenticationToken과 같은 Authentication의 자식 클래스를 사용하여 추가 정보를 넣는 것이다.

    만약 Provider하나의 인증이 실패했다고 하더라도, Authentication Manager는 곧바로 실패 응답을 반환하지 않는다. 가능한 모든 Provider를 호출한 뒤에야 비로소 실패 응답을 반환한다.

  5. UserDetailsService는 사용자의 인증 정보를 로드하는 역할을 한다.

    @Service
    public class CustomUserDetailsService implements UserDetailsService {
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            // 데이터베이스나 다른 소스로부터 사용자 정보를 로드
            if ("user".equals(username)) {
                return User.builder()
                        .username(username)
                        .password("{noop}password") // {noop}은 비밀번호를 암호화하지 않음을 나타냅니다
                        .authorities(Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")))
                        .build();
            } else {
                throw new UsernameNotFoundException("User not found");
            }
        }
    }

    UserDetailsManager 인터페이스는 UserDetailsService를 확장하여 사용자의 추가적인 관리 기능을 제공한다. 이 인터페이스는 사용자의 생성, 수정, 삭제 등의 관리 기능을 포함한다.
    하지만 회원 정보는 외부 DB에 저장하는 경우가 많기 때문에 따로 사용할 일은 적은 듯 하다. 보통 따로 CRUD API를 만들어 유저정보를 관리하는 것이 선호된다.

  6. Password Encoder를 사용하여 패스워드 암호화,보통 BCryptPasswordEncoder()를 많이 사용함.

  7. 인증 정보가 추가된 Autentication객체를 Authentication Manager에게 반환

  8. 모든 Authentication Provider에서 인증이 실패한 경우 실패 메시지 반환, 성공한 경우는 Authentication객체 반환

  9. 최종적으로 인증을 수행한 필터에서 Authentication 객체를 Security Context에 저장함.