-
Notifications
You must be signed in to change notification settings - Fork 8
Spring Security 동작 원리
Yeongseo Na edited this page Jan 14, 2025
·
2 revisions
우리 프로젝트에서 사용하는 Spring Security의 동작 원리에 대해 설명합니다.

- 사용자의 요청이 서버에 도착한다.
- 요청이 서블릿에 도달하기 전에, AuthoticationFilter가 요청을 가로채서 인증을 시작한다.
- 코드 상으로, 스프링 시큐리티에 대한 @Configuration에서 설정한 SecurityFilterChain이 이에 해당한다.
- 필터에서 JWT 토큰 검증, 로그아웃 사용자 처리 등의 일을 수행할 수 있다.
- 앞으로의 인증 과정에서, 인증 정보를 담을 객체 Authentication를 생성하고 manager에 전달한다.
- AuthenticationManager는 등록된 AuthenticationProvider 중 적절한 것을 찾아 인증한다.
- AuthenticationProvider는 UserDetailsService 를 통해 UserDetails를 조회, 반환한다.
- UserDetails 객체에는 사용자명, 비밀번호, 권한 목록 등 인증 및 인가에 필요한 정보가 담겨있다.
- AuthenticationProvider는 제공된 자격 증명과 UserDetails에 담긴 정보를 비교하여 사용자를 인증한다.
- 그리고 그 결과를 Authentication 객체에 담아 반환한다.
- Authentication 객체에는 인증 성공 여부, 권한 목록, 인증 대상(principal), 추가 메타데이터가 담긴다.
- 이때, Authentication의 principal 필드에는 UserDetails가 들어간다.
- Authentication 객체를 SecurityContextHolder에 저장한다.
- SecurityContextHolder는 ThreadLocal 기반으로 동작하여, 각 요청별로 독립된 보안 컨텍스트를 유지하게 한다.
- 컨트롤러나 서비스 계층에서, SecurityContextHolder에 담긴 인증 정보를 활용할 수 있다.
- Principal 객체를 인자로 선언하면, 내부적으로 Authentication.getPrincipal()에 접근해서 Principal 를 인자로 주입해준다.
- @AuthenticationPrincipal 애너테이션을 사용하면, UserDetails를 직접 주입받을 수 있다.
cf. Authentication vs UserDetails 차이
UserDetails는 인증과 인가 자체에 대한 정보만 담지만,
Authentication 는 그 과정에 대한 정보, 추가로 필요한 메타 데이터 등까지 포함한다.
cf. 우리 프로젝트에서는 JWT 토큰을 사용하므로 복잡한 인증이 필요하지 않다.
따라서 토큰과 관련된 과정은 3-5단계를 2단계인 필터에서 진행하도록 단순화했다.
- JWT 는 Header, Payload/Claim, Signature 세개의 구성으로 이루어져있다.
- 각 부분은 Base64로 인코딩되어
.으로 구분된다. - Claim 중에서도 사용자를 식별하기 위한 정보를 Subject 라고 한다. (주로 이메일이나 사용자 id)
👇 claim 예시
{
"sub": "john@example.com", // Subject
"iat": 1516239022, // Issued At
"exp": 1516242622, // Expiration Time
"name": "John Doe", // Custom Claim
"role": "ADMIN" // Custom Claim
}
스프링에서 JWT를 만들 때는 Jwt builder를 사용해서 만든다.
public String generateToken(String email, TokenType tokenType) {
Claims claims = Jwts.claims().setSubject(email); // subject 설정
Date now = new Date();
Date expiredDate = new Date(now.getTime() + tokenType.getExpireTime());
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expiredDate)
.signWith(SignatureAlgorithm.HS512, this.secretKey)
.compact();
}- 🪽 Flyway로 DB 마이그레이션 관리하기
- 🐳 TestContainers 도입기
- 🔑 Spring Security 동작 원리
- 🔧 브랜치 전략 변경 1) ‐ Git Flow로 변경
- 🪵 메트릭과 로그 수집
- 🍂 e2e 테스트, 그때는 맞고 지금은 틀린 이유
- 🚘 프로젝트 중간에 ci 도입하기
- 🐘 gradle로 의존성 '잘' 설정하기
- 📃 Alloy를 이용한 Loki로의 로그 전송
- 🔧 브랜치 전략 변경 2) ‐ 우리 팀만의 전략으로
- ⛰️ K6 선택 이유와 주요 성능 지표
- 🤔 Test Fixture를 체계화하면서 겪은 고민들
- ⚙️ 성능 테스트 설계 과정