ㅅㅇ

[Spring Securtiy] SpringSecurity 의 인증 및 인가 보안 처리 과정 본문

SW_STUDY/SpringBoot

[Spring Securtiy] SpringSecurity 의 인증 및 인가 보안 처리 과정

SO__OS 2023. 3. 8. 19:44

아래 아키텍처와 같이 SpringSecurity 의 여러 필터 처리 과정을 통해서 우린 사용자의 로그인 요청과 특정 자원 요청에 대해 인증 및 인가 보안 처리를 할 수 있다.

 

https://gngsn.tistory.com/160

 

1. 인증 (로그인 요청)

1. 클라이언트에서 로그인 요청. id, password 을 전달 받음.


- DelegatingFilterProxy

 

2. DelegatingFilterProxy는 springSecurityFilterChain 이라는 이름을 가진 Bean 을 찾음.

 

3. DelegatingFilterProxy는 그 이름을 가진 빈인 FilterChainProxy에게 인증요청을 위임


- FilterChainProxy 

4. FilterChainProxy 의 필터가 인증 인가 보안 처리 수행 시작!!


- SecurityContextPersistenceFilter

 

5. loadContext 함수를 호출해 SecurityContext 가 있는지 확인

 

6. HttpSecurityContextRepository 클래스가 새로운 컨텍스트 생성(SecurityContextHolder) 한다.

 

7. 새로운 SecurityContext를 저장해서 SecurityContextHolder에 저장 (이때 SecurityContext 객체는 null)

 

8. 그 다음 필터로 이동(chain.doFIlter)


- UsernamePasswordAuthenticationFilter

 

9. 요청 정보를 받아 정보를 추출하고 ID + PASSWORD를 담은 인증 객체 Authentication 을 생성.

 

10. AuthenticationManager 인증관리자에게 인증객체를 넘기며 인증처리를 위임한다.

 

11. AuthenticationManager는 적절한 Provider(AuthenticationProvider)에게 인증처리를 위임한다.

 

12. 해당 Provider는 input 정보(id, password)를 가지고 실제 인증 처리(아이디 검증, 비밀번호 검증, 추가 검증) 역할을 한다.

 

13. 아이디 검증

  a. UserDetailsService 인터페이스에게 loadUserByUsername(username) 요청

 

  b. loadUserByUsername(username) 메서드를 호출해서 유저객체를 요청한다.

 

    i. Repository에서 findById() 메서드로 로그인한 ID 을 통해 DB에서 유저 객체 조회를 한다.

    ii. (해당 유저객체 존재 x) UsernameNotFoundException이 발생.

       ->  UsernamePasswordAuthenticationFIlter에서 예외를 처리

       -> FailHandler()에서 후속처리

    iii. (해당 유저객체 존재) UserDetails 타입으로 반환된다.

 

14. Password 검증

 

    a. 인증객체의 Password와 반환받은 UserDetails의 Password를 비교

    b. (일치하지 않을 경우) BadCredentialException 발생 후 인증 실패

    c. (일치) 성공한 인증객체( Principal(UserDetials) 와 authorities를 담은 인증 후 토큰 객체 Authentication )   를           UsernamePasswordAuthenticationFilter에 전달한다.

     - Principal(UserDetials), Credentials, Authorities, Authenticated 담을 수 있다.

 

15. UsernamePasswordAuthenticationFilter에서 SecurityContextHolder 객체 안의 SecurityContext 에 저장

   (이후 전역적으로 SecurityContextHolder 에서 인증객체를 사용가능하게 된다.)

 

16. 그 다음 필터로 이동(chain.doFIlter)


- SecurityContextPerstenceFilter

 

17. Client에게 응답하는 시점에서 아까 저장한 SecurityContext를 불러와, 최종적으로 HttpSession에 저장

 

18. SecurityContextHolder.clearContext() 메서드 호출. SecurityContext 인증 정보를 초기화

 

19. 클라이언트에게 sessionID(JSESSION ID) 와 함께 응답하게 된다.

      a. 이후 요청에서 요청 쿠키에서 JSESSION ID 정보를 통해 현재 로그인 정보가 저장되어 있는 지 확인.

           저장되어 있고 유효하면 인증 처리를 해줌.

2. 인증 후 특정 페이지 이동 (자원 Request)

1. 사용자가 자원 접근(Request)


- SecurityContextPersistenceFilter

 

2. loadContext 로 Session에서 SecurityContext를 꺼내온다.

 

3. 이를 꺼내어 SecurityContextHolder에서 저장. (SecurityContext안에 Authentication 객체가 존재하면 계속 인증을 유지하는 것.)

 

4. 다음 필터 수행(chain.doFilter)


- FilterSecurityInterceptor

 

5. 인증 여부를 확인한다.

    a. 인증객체를 가지고 있는지 확인한다.

        i. (인증객체가 null) AuthenticationException 발생

        ii. ExceptionTranslationFilter에서 해당 예외를 받아서 다시 로그인 페이지로 이동하던가 후처리를 해준다.

    b. (인증객체가 있을 경우) SecurityMetadataSource는 자원에 접근하기 위해 설정된 권한정보를 조회해서 전달해준다.

 

6. 권한 정보를 조회한다.

    a. (권한정보가 null) 권한 심사를 하지 않고 자원 접근을 허용한다.

    b. (권한 정보가 있을 경우) AccessDecisionManager 에게 권한 정보를 전달하여 인가 처리를 위임한다.

 

7. AccessDecisionManager가 내부적으로 AccessDecisionVoter(심의자)에게 정보decide(authentication, object, configAttributes)를 전달하며 심의 요청을 한다.

 

8. Voter들은 정보들을 가지고 권한 판단을 심사하고 결정 방식(승인/거부/보류)을 반환한다.

 

9. AccessDecisionManager에서 반환된 결과를 가지고 최종적으로 사용자가 해당 자원에 접근이 가능한지 판단한다.

    i. (접근이 거부되었을 경우) AccessDeniedException이 발생한다.

    ii. ExceptionTranslationFilter에서 해당 예외를 전달하여 다시 로그인 페이지로 이동하는 등 후처리를 해준다.

 

9. (접근이 승인되었을 경우) FilterSecurityInterceptor에 승인여부 반환. 자원 접근이 허용된다.

 

[참고]
https://catsbi.oopy.io/f9b0d83c-4775-47da-9c81-2261851fe0d0