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를 직접 설정하는 것이다.
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 Boot' 카테고리의 다른 글
Spring Security 인가 처리 - AccessDecisionManager, AccessDecisionVoter (0) | 2022.08.04 |
---|---|
Spring Security 인증 처리 (2) Filter, AuthenticationManager, AuthenticationProvider, WebSecurityConfigure (0) | 2022.07.26 |
Swagger로 API 문서 자동화하기 +JWT 설정 (0) | 2022.05.26 |
[Spring Boot] JUnit5 로 테스트 하기 (0) | 2022.05.25 |
JWT 를 간단히 정리해보자 (1) | 2022.05.17 |