Spring Security를 공부하기에 앞서

인증의 기본이 되는 Authentication 객체와 이를 저장하는 SecurityContextHolder에 대해 알아보겠습니다.

 

 

 

Authentication

인증 된 사용자에 대한 상세 정보, 즉 통행증 역할을 한다.

SecurityContextHolder 내부의 SecurityContext에 저장된다.

 

Authentication을 구현하는 객체들은 대체로 메서드 이름 뒤에 AuthenticationToken이 붙는다.

이를 인증 토큰이라고 부른다.

 

 

- 주요 용어 정리

 

  • Principal: 보호 된 리소스에 접근하는 사용자 식별자(아이디)
  • Credentials: 인증을 위해 필요한 정보(비밀번호)
  • GrantedAuthority: 인증 된 사용자의 인증 정보(역할 등)을 표현
  • SecurityContext: Authentication 객체를 포함하고 있으며, SecurityContextHolder를 통해 접근할 수 있다.

 

 

- Authentication 인터페이스가 제공하는 핵심 메서드

 

  • Object getPrincipal()
  • Object getCredentials()
  • Collection<? extends GrantedAuthority> getAuthorities()
  • Object getDetails(): 인증 관련 부가 정보들

 

 

 

 

SecurityContextHolder

Spring Security 인증 모델의 핵심으로 SecurityContext가 포함된다.

 

SecurityContextHolder

 

인증 된 사용자의 세부 정보를 저장하는 곳으로

어떤 값이든지 값을 포함하고 있다면 현재 인증 된 사용자로 사용된다.

 

사용자가 인증되었음을 나타내는 가장 간단한 방법은 SecurityContextHolder를 직접 설정하는 것이다.

JUnit5로 SecurityContextHolderTest를 작성해보았다.

(JUnit5 사용법 참고) https://e-una.tistory.com/52

 

import org.junit.jupiter.api.*;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.Collection;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class SecurityContextHolderTest {

    private String username;

    private String password;

    private String USER_ROLE;

    @BeforeAll
    void setup() {
        username = "una";
        password = "password123";
        USER_ROLE = "ROLE_USER";
    }

    @Test
    @Order(1)
    void 인증된_사용자정보를_셋팅한다() {
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        Authentication authentication =
                new TestingAuthenticationToken(username, password, USER_ROLE);
        context.setAuthentication(authentication);

        SecurityContextHolder.setContext(context);
    }

    @Test
    @Order(2)
    void 인증된_사용자정보에_접근한다() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

        assertThat(authentication.isAuthenticated(), is(true));
        assertThat(authentication.getName(), is(username));
        assertThat(authentication.getCredentials(), is(password));
        assertThat(authentication.getAuthorities(), containsInAnyOrder(
                hasProperty("authority", is(USER_ROLE))));
    }
}

 

 

 

시큐리티 인증을 구현하는 방법은 아래와 같다.

 

1. (Authentication을 구현하는 객체) 블라블라AuthenticationToken 클래스 인증 정보를 생성한다.

테스트에서는 간단히 String 에 직접 값을 넣어서 작성하였지만

사용자 정보가 올바른지 인증하는 방법은 JWT가 될 수도 있을거고 여러 방법이 있을 것이다.

 

2. 생성 된 블라블라AuthenticationToken 객체는 SecurityContextHolder를 통해 디폴트로 Thread-Local 영역에 저장된다.

 

 

 

 

Thread-Local

  • Java.lang 패키지에서 제공
  • thread 내부에서 사용되는 지역변수인 thread-local 변수 제공
  • thread 수준에서 공유하는 데이터 저장소로 파라미터로 넘기지 않더라도 접근이 가능하다.
  • 시큐리티 외에는 트랜잭션에 사용된다.

 

즉, SecurityContextHolder는 파라미터로 Authentication 을 전달받지 않더라도

ThreadLocal 영역에 저장 된 Authentication 객체(현재 인증 처리 된 사용자 정보)를 꺼내서 사용할 수 있는 것이다.

 

SecurityContextHolder.getContext().getAuthentication().getName();

 

 

 

스프링 어플리케이션에서는 대부분의 경우 요청  thread 한개가 할당된다.

만약 Multi thread를 사용해야하는 경우, SecurityContextHolder에 다른 전략을 셋팅해야한다.

 

Strategy 종류

 

  • MODE_THREADLOCAL : 기본 설정
  • MODE_INHERITABLETHREADLOCAL : 현재 스레드에서 하위에 생성 된 스레드까지 SecurityContext 공유
  • MODE_GLOBAL : 어플리케이션 전체에서 SecurityContext 공유

 

SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL);

 

(참고 내용)

ThreadLocal 사용 시, ThreadLocal 변수에 저장 된 데이터 사용이 끝나면 반드시 해당 데이터를 삭제해야한다.

삭제하지 않는 경우에 재사용 되는 스레드가 올바르지 않은 데이터를 참조할 수 있는데

시큐리티에서는 FilterChainProxy가 요청 처리 후 스레드 삭제를 보장한다.

 

 

 

 

정리

 

- 인증 된 사용자에 대한 상세 정보를 저장하는 통행증 Authentication 객체

- 이를 저장하는 저장소 SecurityContextHolder

- SecurityContextHolder는 디폴트로 Authentication 객체를 Thread Local 영역에 저장

 

 

 

 

 

참고

https://gregor77.github.io/2021/04/19/spring-security-01/

https://escapefromcoding.tistory.com/475?category=1174547

Spring security document

 

 

 

 

복사했습니다!