SecurityContextHolder/SecurityContext/Authentication관계SecurityContextHolder └── SecurityContext (보안 컨텍스트) └── Authentication (인증 정보) ├── Principal (사용자 정보) ├── Credentials (비밀번호, JWT 등) └── Authorities (권한 정보)。
AuthenticationSecurityContextSecurityContextHolder
SecurityContextHolder:
。현재 실행중인thread의SecurityContext를 저장 및 관리하는저장소 객체
▶ThreadLocal을 기반으로 동작하여Thread별SecurityContext객체를 독립적으로 관리
。스레드별인증이 완료된 개별 사용자의인증정보가 포함된Authentication 구현체가 저장된사용자 별 고유한 SecurityContext객체를 저장.
▶어플리케이션어디에서든지 현재 로그인한사용자 인증정보가 포함된Authentication 구현체를 가져올 수 있음.
SecurityContextHolder가Spring Bean이 아닌ThreadLocal기반인 이유?
。SecurityContextHolder를 만약에Spring Bean으로 등록하는 경우,전체 사용자가 해당SecurityContextHolder를싱글톤으로서 서로 공유하는 문제점이 존재
▶ 다른사용자가SecurityContext의Authentication의 보안이 필요한필드에 접근 가능.
。따라서,ThreadLocal을 통해 각사용자의스레드마다 독립적인Security Context를 제공
▶Tomcat의 경우사용자 요청당스레드하나를 할당하므로 각사용자는 고유한Security Context를 가질 수 있다.
ThreadLocaljava.lang.ThreadLocal
。스레드마다 독립적인값을 저장할 수 있게하는Java Class
▶VIEW의Model 객체같은 느낌
。ThreadLocal을 통해Thread Safe를 제공
▶ 동일한SecurityContextHolder내 에서도스레드 별로 서로 다른Security Cotext를 가질 수 있음.
。해당ThreadLocal가 가진SecurityContext내Authentication 객체를 유일하게 관리하도록 설정하여동시참조를 차단
SecurityContext:
。현재로그인한사용자의인증정보(Authentication)를 포함하는Container역할의 객체
▶AuthenticationProvider에 의해 인증이 완료된Authentication 구현체를 저장
。SecurityContextHolder에서스레드 별로 저장 및 관리가 수행되며SecurityContextHolder를 통해 접근 가능.
。SecurityContext내부에 현재스레드로로그인중인Authentication 객체의principal을Controller의@AuthenticationPrincipal을 통해UserDetails 구현체에의존성 주입하여 현재로그인 사용자의principal정보를 전달이 가능
WAS를 통한 별도의스레드로 접속한 각사용자는인증이 완료되어 완전히 생성된Authentication 구현체를SecurityContextHolder내스레드별로 구분된Security Context에 저장
。인증이 완료된Authentication 구현체는스레드별로 할당된SecurityContext에 저장되므로, 다른스레드에서 해당Authentication 구현체를 조회할 수 없다.
Authentication:
org.springframework.security.core.Authentication
。Spring Security에서 사용자의Authentication정보를추상화하는인터페이스
▶사용자정보(principal) ,권한정보(authorities) ,토큰(credentials) 등을 포함.
。Authentication의authorities는 여러개의권한(Collection<GrantedAuthority>)를 가질 수 있다.
▶ 하나의User는 여러역할을 가질 수 있음.
인증을 수행 시로그인 정보를credentials에 포함 후 생성하여 활용되고,인증이 끝난 후 완전한Authentication 구현체로서SecurityContextHoder내스레드별 SecurityContext에 저장
。로그인수행 시credentials를 포함한Authentication 구현체를 생성 후 인증이 완료된 경우DB에서 해당사용자 정보를 포함하는UserDetails 구현체를 생성하여principal과authorities를 포함한 완전한Authentication 구현체를 생성 후SecurityContextHolder의현재 스레드의SecurityContext로 등록
Authentication 구현체는Spring Bean이 아닌,ThreadLocal 환경에서 관리
。Security Context Holder가ThreadLocal 기반인 이유
。Authentication을Spring Context내 저장 시싱글톤으로 관리되는 경우 해당Authentication으로 다른 사람이의존성 주입받아서 해당Authentication정보를 확인하는단점이 존재하므로.
▶ThreadLocal은 각각의사용자의스레드마다SecurityContext를 통해 서로 격리되어 다른스레드에서Authentication 구현체를 조회할 수 없다.
Authentication 구현체구조
ex)클라이언트에서 전송된HTTP Request의HTTP Basic Authentication인증정보가 포함된 경우의서버에서 생성된Authentication 구현체의 구조
principal:
。인증된사용자의세부정보를 의미.
▶ 보통UserDetails 객체의 내용을 포함
。CurrentUser principal = (CurrentUser) authentication.getPrincipal();
▶ 다음처럼 가져온 후UserDetails 객체로캐스팅해서 활용
credentials
。사용자가인증시 입력한인증정보를 포함 ( ex.username,password,JWT Token)
。초기HTTP Request의Authorization Header에 포함된인증정보를credentials에 포함한Authentication 객체생성 및AuthenticationManager에 의한검증수행
▶검증이 끝난 후Authentication 객체는principal과authorities는 포함하고credentials는 보안을 위해null로 설정
authorities
。principal에 정의된 사용자가 보유한역할, 권한목록을 지시.
。Collection<GrantedAuthority> 객체를 포함하여 여러역할을 가질 수 있다.
▶ 주로"ROLE_USER" , "ROLE_ADMIN"등의권한정보를 포함.
GrantedAuthority
。Spring Security에서사용자 권한 정보(Role/Authority)를 표현하는인터페이스
▶ 현재인증된사용자의권한을 포함하는컨테이너 객체역할을 수행
。Role을 설정 시 예전에는prefix:ROLE_을사용자가 정의해야했지만, 현재는Spring내부에서 자동으로 적용
▶ 사용자가MEMBER를권한명으로 설정 시ROLE_MEMBER로 자동으로 반영
ex)ROLE_ADMIN,ROLE_MEMBERpublic interface GrantedAuthority extends Serializable { String getAuthority(); }。
GrantedAuthority는 단순히문자열 권한("ROLE_USER","ROLE_ADMIN")을감싸는역할의객체UserDetails userDetails = User.builder() .username("user") .password("1234") .roles("USER") .build();。해당
UserDetails 구현체내"ROLE_USER"로서GrantedAuthority 구현체로 저장
▶ 주로UserDetails 구현체,Authentication 구현체내부에Collection<GrantedAuthority>로서 저장되어권한 인가시 활용
SimpleGrantedAuthority
。Spring Security에서 기본적으로 사용하는GrantedAuthority 구현체
▶getAuthority()를메서드 오버라이딩및사용자 권한(Role / Authority)을 단순한문자열형태로 저장하는객체@Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(new SimpleGrantedAuthority("ROLE_" + role.name())); }。 보통
UserDetails에서getAuthorities()를 통해 반환됨
▶{ ROLE_USER }