✨KB IT's Your Life✨ 5기 TIL 기자단 활동
KB IT's Your Life 5기에서 학습한 내용을 복습하고자 정리한 글
기간: 8/26~9/1
학습 내용: Spring Security & JWT
Spring Security
Spring 프레임워크를 기반으로 하는 애플리케이션에서 인증(Authentication)과 인가(Authorization)를 구현하기 위한 강력하고 유연한 보안 프레임워크
Authentication(인증)
사용자가 누구인지 확인하는 과정
로그인 과정 = 인증 과정
다양한 인증 프로바이더 통해서 인증 처리
Authorization(인가 = 권한부여)
인증된 사용자가 특정 자원에 접근 가능한 권한이 있는지 확인하는 과정
인증(Authentication) 이후에 이루어지는 과정
로그인한 사람의 역할에 따라 특정 작업 할 수 있는 권한 추가 부여
Spring Security Authentication Architecture

SecurityContextHolder
- 애플리케이션 전반에서 인증된 사용자 정보에 접근하여 관리하는 중앙 저장소
AuthenticationFilter
- Spring Security의 인증 필터(Authentication Filter)
- 들어오는 요청을 가로채고 인증처리 시작
- 2번 과정: UsernamePasswordAuthenticationToken을 생성
UsernamePasswordAuthenticationToken
- 사용자 이름과 비밀번호를 포함하는 객체
- 3번 과정: AuthenticationManager에게 해당 토큰을 전달
- 10번 과정: 반환된 Authentication 객체를 SeucirtyContextHolder에 저장
AuthenticationManager
- Spring Security에서 모든 인증 요청 처리 담당
- 4번 과정: AuthenticationProvider에게 실제 인증 작업 위임
ProviderManager
- 여러 AuthenticationProvider가 존재하는 경우 Providers 간 조정 담당
- 첫 번째 AuthenticationProvider에서 인증 실패 시 다음 AuthenticationProvider로 인증 요청 전달
- 9번 과정: 인증 성공 시 인증된 사용자 정보가 포함된 Authentication 객체를 Authentication Filter로 반환
AuthenticationProvider
- 사용자 정보를 검증하는 다양한 방식 제공 가능
- 특정 인증 방식을 담당하는 여러 AuthenticationProivder 존재 가능
- 5번 과정: UserDetailsService를 사용하여 사용자 세부 정보 가져옴
- 8번 과정: UserDetailsService로부터 반환된 UserDetails 객체를 사용하여 사용자 검증
- 입력된 비밀번호와 DB에 저장된 비밀번호가 일치하는지 확인
UserDetailsService
- 사용자 정보를 가져오는 인터페이스
- DB에서 사용자 정보 조회 담당
- 7번 과정: UserDetails 객체 반환
UserDetails
- 사용자의 계정 정보(사용자 이름, 비밀번호, 권한 등)를 포함하는 객체
CSRF 공격(Cross-Site Request Forgery)
- 인터넷 사용자(희생자)가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 웹 사이트에게 요청하게 만드는 공격
- 방어책 - CSRF 토큰 운영
-
form 페이지 GET 요청 시 form 내에 인증토큰 같이 전송
-
서버가 보낸 인증토큰과 동일한 토큰 있는 경우에만 POST요청으로 인식
- 서버가 토큰을 보내고, 클라이언트에서 해당 토큰을 다시 보내게 되는 구조
-
해당 토큰이 없다면 에러 발생
⇒ Spring Security 사용시 디폴트로 사용하는 것으로 설정된다!
Security Filter Chain
Spring Security는 클라이언트 요청이 서버로 전달하기 전에 여러 개의 필터를 통해 보안 검사를 수행
특정 순서로 배열되어 있으며, 요청이 각 필터를 통과하며 점차 보안 검사 강화되는 구조

SecurityContextPersistenceFilter
- request 발생 시 SecurityContext 객체의 생성, 저장, 조회를 담당하는 필터
- 새로운 SecurityContext를 생성하여 SecurityContextHolder에 저장
- 익명 사용자의 경우
- AnonymousAuthenticationFilter에서 AnonymousAuthentication Token 객체를 SecurityContext에 저장
- 인증 시
- UsernamePasswordAuthenticationFilter에서 인증 성공 후 SecurityContext에 UsernamePasswordAuthentication 객체를 Authentication 객체와 함께 저장
- 인증이 완료되면 Session에 SecurityContext를 저장하고 response
- 인증 후
- Session에서 SecurityContext를 꺼내서 SecurityContextHolder에 저장
- SecurityContext 내에 Authentication 객체가 존재하면 인증 유지
LogoutFilter
- 유저의 로그아웃 처리 진행
- 설정된 로그아웃 URL로 오는 요청을 감시, 해당 유저를 로그아웃 처리
- 사용자의 인증 세션 무효화 및 관련 쿠키 삭제 & 로그아웃 성공 페이지로 redirection
UsernamePasswordAuthenticationFilter
- 설정된 URL로 들어오는 요청을 감시, 유저 인증 처리
- AuthenticationManager와 연결되어 있음
- 인증 성공시 AuthenticationSuccessHandler 실행
- 인증 실패시 AuthenticationFailureHandler 실행
DefaultLoginPageGeneratingFilter
- 사용자 정의 로그인 페이지가 제공되지 않은 경우 기본 로그인 페이지를 생성하여 사용자에게 제공
BasicAuthenticationFilter
- HTTP 기본 인증(HTTP Basic Authentication) 헤더를 사용하여 사용자를 인증하는 필터
- 요청 헤더에 포함된 자격 증명을 분석하여 사용자 인증
RememberMeAuthenticationFilter
- 사용자가 Remember Me 기능을 사용하여 로그인한 경우 해당 정보를 처리하여 사용자를 인증하는 필터
- 로그인 상태를 유지하여 다시 로그인하지 않고 사이트 방문 가능하게 함
- SecurityContext에 인증(Authentication) 객체가 존재하는지 확인
- RememberMeServices를 구현한 객체의 요청이 있을 경우, Remember-Me 인증 토큰으로 Context에 주입
SecurityContextHolderAwareRequestFilter
- SecurityContextHolder를 사용하여 현재 사용자 인증 정보에 접근 가능한 기능을 제공
- 요청 객체에 보안 관련 정보 추가
AnonymousAuthenticationFilter
- 인증되지 않은 사용자에 대해 익명 권한 부여
- SecurityContextHolder에 Authentication 객체가 있는지 확인
- 필요한 경우 Authentication 객체 주입
SessionManagementFilter
- 요청 시작 이후 인증된 사용자인지 확인
- 인증된 사용자인 경우 SessionAuthenticationStrategy를 호출하여 세션 고정 보호 메커니즘을 활성화하거나 동시 로그인 확인 등의 세션 관련 활동 수행
ExceptionTranslationFilter
- 필터체인 내에서 발생하는 모든 보안 예외(AccessDeniedException, AuthenticationException)를 처리
인증 정보가 없는 경우
SecurityContextHolder, RequestCache, AuthenticationEntryPoint 확인 후 없는 경우 401 Error 처리(로그인 없이 접근한 경우)
권한이 없는 경우
AccessDeniedHandler를 통해 403 Error 처리(권한 부족)
FilterSecurityInterceptor
- 요청이 실제로 처리되기 전에 권한 부여 최종 검증
- 사용자가 가진 권한으로 요청된 URL, 메소드에 접근 가능한지 확인
- AccessDecisionManager와 SecurityMetadataSource와 함께 작동
Authentication
SecurityContext
- 인증된 사용자와 관련된 보안 정보 저장
- 세션을 통해 유지되며, SecurityContextHolder를 통해 접근 가능
Authentication 객체
- 인증된 사용자에 대한 정보를 포함하는 객체
- 사용자 이름, 권한, 인증 방식 등 포함
AuthenticationManager
- 사용자의 자격 증명을 검증하여 인증 수행
- AuthenticationProvider에게 인증 요청 위임
AuthenticationProvider
- 사용자의 자격 증명을 실제로 검증하는 구성 요소
- 여러 종류의 AuthenticationProvider 존재 가능(다른 방식으로 자격 증명 처리)
UserDetailsService
- 사용자의 세부 정보를 로드하는 인터페이스
- DB에서 사용자 정보를 불러와 UserDetails 객체로 반환
UserDetails
- 사용자 계정의 세부 정보를 포함하는 객체
- 사용자 이름, 비밀번호, 계정 상태, 권한 등 포함
Authorization
AuthenticationEntryPoint
- 인증되지 않은 사용자가 보호된 자원(resource)에 접근하려고 할 때 호출
- 인증이 필요한 페이지로 리디렉션 또는 로그인 페이지로 이동시키는 역할
AccessDeniedHandler
- 인증된 사용자가 접근 권한이 없는 자원(resource)에 접근하려고 할 때 호출
- 권한이 없음을 알리는 메시지 반환 또는 에러 페이지로 리디렉션
AccessDecisionManager
- 최종적으로 사용자가 요청한 자원(resource)에 접근 가능한지를 결정하는 컴포넌트
- 여러 DecisionVoter로부터 받은 투표 결과를 기반으로 접근 허용 여부 결정
DecisionVoters
- 각 Voter는 특정 조건에 따라 사용자의 접근을 허용할지, 거부할지 결정
SecurityMetadataSource
- 보호된 자원(resource)와 관련된 메타데이터를 제공하는 인터페이스
- 메타데이터를 통해 어떤 자원에 어떤 권한이 필요한지 결정
AffirmativeBased
- 여러 DecisionVoter 중 하나라도 허용(Vote ALLOW)을 결정하면 접근을 허용하는 전략
RoleVoter
- 사용자의 역할을 기반으로 접근 허용 여부를 결정하는 DecisionVoter의 일종
JWT(JSON Web Token)
웹 애플리케이션에서 사용자 인증 및 권한 부여 관리에 굉장히 널리 사용되는 토큰 기반 인증 메커니즘
JWT 사용 이유
자기 포함(self-contained)
- JWT는 필요한 정보를 자체적으로 포함하고 있어, 별도의 데이터베이스 조회 없이도 토큰만으로 인증 및 권한 부여에 필요한 정보 확인 가능
- 시스템 성능 향상 및 분산 시스템에서 효율적으로 사용 가능
보안
- JWT는 서명된 토큰 → 수신자가 토큰의 무결성 검증 가능
- 서명은 대칭키(HMAC) 또는 비대칭키(RSA)를 사용하여 생성
→ 이를 통해 토큰이 변조되지 않았음을 보장
확장성
- 경량의 텍스트 포맷인 JSON으로 구성되어 있어, 다른 시스템 간의 데이터 전송시 유리
- HTTP 헤더에서 쉽게 전달 가능하며 RESTful API와 같은 무상태(stateless) 애플리케이션에서 효과적으로 사용 가능
유연성
- JWT는 클레임(Claims)이라고 불리는 key-value 쌍을 포함할 수 있어 다양한 인증 정보 저장 및 전달 가능
- 다양한 요구사항에 맞게 확장 및 사용자 정의 가능
언어 및 플랫폼 독립성
- JSON 포맷을 사용하기 때문에 대부분의 프로그래밍 언어에서 쉽게 생성하고 파싱 가능
- 클라이언트와 서버 간 서로 다른 기술 스택을 사용하더라도 JWT는 원활하게 동작 가능
세션 VS JWT
주로 사용자 인증과 관련된 정보를 서버 - 클라이언트 간 어떻게 관리하고 전달하는지 차이
| 항목 | 세션 | JWT |
|---|
| 상태관리 | 서버 상태 기반(Stateful) | 무상태(Stateless) |
| 데이터 저장 위치 | 서버 측 저장 | 클라이언트 측 저장 |
| 확장성 | 서버 확장시 세션 동기화 필요 | 서버 확장시 상태유지 불필요 |
| 보안 | 서버 측 관리로 안전, 세션 ID 탈취 위험은 존재 | 클라이언트 측 보안 중요, 토큰 탈취 시 위험 존재 |
| JWT 사용 시 세션 사용 | 일반적으로 사용 X | 리프레시 토큰 관리, 서버측 상태 관리 필요시 사용 |
데이터 저장 위치
세션
- 세션이 유지되는 동안 서버 램에 상태 정보를 저장
- 사용자가 로그인하면 서버는 세션을 생성하고 세션 ID를 생성하여 클라이언트(주로 브라우저)에 쿠키로 전달
- 클라이언트는 이후 요청 시 세션 ID를 서버에 전달하며, 서버는 세션 ID를 이용해 클라이언트를 구분하고 저장된 세션 정보를 조회
JWT
- 사용자가 로그인하면 서버는 JWT를 생성하여 클라이언트에 전달
- 클라이언트는 이후 요청시 JWT를 서버에 전달하며, 서버는 JWT를 검증하여 사용자의 신원을 확인
- 서버는 세션 정보를 따로 저장 X, 토큰 자체에 필요한 정보가 포함되어 있음
서버 부담
세션
- 서버가 세션 정보를 관리해야하기 때문에 사용자가 많아질수록 서버의 메모리 & DB에 부하 증가
JWT
- 서버는 토큰 검증 작업만 수행
- 서버가 세션 저장할 필요 X → 서버 부담 감소
확장성
세션
- 여러 서버(또는 서버 클러스터)를 운영하는 경우, 모든 서버가 동일한 세션 정보를 공유 필요 → 세션 데이터를 공유하는 추가적인 복잡성 필요
JWT
- 서버 간 세션 공유 필요 X → 여러 서버에서 쉽게 확장 가능
보안
세션
- 서버에 세션 정보 저장 → 서버가 보안에 민감해짐
- 세션 ID 유출 시 보안 문제 발생 가능
JWT
- 자체적으로 서명 포함되어 있어 위/변조 방지
- 토큰 유출될 경우 만료 전까지 유효하므로 유출시 보안 문제 발생 가능
- 적절한 토큰 만료시간 설정 중요
토큰의 정보
세션
- 서버에 저장된 세션 정보만으로 사용자 상태 유지 → 클라이언트에는 단순한 세션 ID만 전달
JWT
- 사용자 정보와 Claim을 포함할 수 있어 클라이언트-서버 간 추가적인 데이터 전송 감소
- JWT에 포함된 정보가 클라이언트에 노출될 수 있으므로 민감 정보는 포함해서는 X
- Claim: JSON 포맷에 해당하는 key-value 형태의 데이터
JWT의 장단점
장점
- 자체 포함(self-contained)
- 모든 필요한 정보가 토큰에 포함되어있어 추가적인 DB 조회 필요 X
- 확장성
- 분산 시스템에서 중앙 인증 서버를 거치지 않고도 사용자가 인증된 상태 유지 가능
- 보안
- 서명된 토큰이므로, 토큰이 변조되지 않았는지 검증 가능
단점
- Payload 암호화 부족
- Payload가 인코딩만 되어있고 암호화 되어있지 않아 누구든 내용 확인 가능
- 만료되지 않은 토큰
- JWT는 기본적으로 무상태(Stateless) 토큰 → 만료되기 전까지는 유효
- 서버에서 강제로 토큰 무효화하기 어려움
- 토큰 크기
- JWT는 일반적으로 쿠키나 URL에 저장
- Payload에 많은 데이터를 포함할 경우 토큰의 크기가 커질 수 있음
JWT 구조

Header.Payload.Signature 3가지로 구성
- 각 부분은 Base64URL로 인코딩되어 표현되며, 각 구성요소는 .으로 구분
Base64 Encoding
이진 데이터(Binary Data)를 공통 ASCII 영역의 문자열(Text)로 변환하는 encoding 방식
alg 와 typ으로 구성
- alg: 해싱 알고리즘(algorithm). 서명(Signature) 및 토큰 검증에 사용
- typ: 토큰 타입(type)
HS256(HMAC with SHA-256)
특정 데이터를 SHA-256 알고리즘으로 해시하고 비밀 키를 더해 안전한 서명을 만드는 알고리즘
256bit(=32byte)길이로 암호화
SHA-356
데이터를 암호화해서 해시 값을 만드는 알고리즘
데이터를 특정 길이의 고유한 문자열로 변환
HMAC
해시 값을 안전하게 하기 위해 비밀 키를 추가하는 방식
해시(Hash)
데이터를 고정된 길이의 고유한 값으로 변환하는 것
해당 값은 데이터에 따라 다르며, 데이터 식별 및 무결성 확인에 사용
Payload
payload = 짐, 트럭에 짐 싣고 갈때 실린 짐을 payload라고 함!
토큰에서 사용할 정보의 조각들인 key-value 형태의 Claim이 담겨있는 부분
Claim
토큰 안에 포함된 정보의 주장, 진술을 의미하기 때문에 claim이라는 단어 사용
토큰을 사용하는 시스템 내에서 특정 정보를 인증하거나 확인하는데 사용되므로, 해당 정보들이 주장된다는 의미에서 claim 사용!
등록된 클레임(Registered Claim)
토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들
모두 선택적으로 작성 가능(사용 권장)
- iss: 토큰 발급자(issuer)
- sub: 토큰 제목(subject). unique한 값 사용(주로 사용자 이메일)
- aud: 토큰 대상자(audience)
- exp: 토큰 만료 시간(expiration).
NumericDate 형식으로 되어있어야 함
- nbf: 토큰 활성 날짜(not before)
- iat: 토큰 발급 시간(issued at). 토큰 발급 이후의 경과 시간
- jti: JWT 토큰 식별자(JWT ID). 중복 방지를 위해 사용하며 일회용 토큰(Access Token) 등에 사용
공개 클레임(Public Claim)
사용자 정의 클레임
공개용 정보를 위해서 사용
비공개 클레임(Private Claim)
사용자 정의 클레임
서버와 클라이언트 사이에 임의로 지정한 정보를 저장
Signature
토큰을 인코딩하거나 유효성 검증 시 사용하는 고유한 암호화 코드
Header와 Paylaod, Secret Key를 기반으로 생성
해당 토큰이 변조되지 않았음을 확인하기 위한 메커니즘
→ 제대로 만들어진 데이터인지, 위조된 데이터는 아닌지 검증하기 위함
서명 생성 과정
- Header와 Payload의 값을 각각 BASE64로 인코딩
- 인코딩한 값을 Secret Key를 이용해 Header에서 정의한 알고리즘으로 해싱
- 해싱한 값을 다시 BASE64로 인코딩하여 생성
JWT 사용 사례
사용자 인증(Authentication)
- 가장 일반적인 JWT 사용 사례
- 사용자 로그인 → 서버는 JWT를 생성하여 사용자에게 전달
- 이후 모든 요청은 JWT를 사용하여 인증이 이뤄지며, 서버는 JWT를 검증하여 사용자의 신원 확인
권한 부여(Authentication)
- JWT는 사용자의 권한 정보를 담고 있어, 특정 자원(resource)에 대한 접근 권한 제어에 사용
- 사용자에게 부여된 역할(role)에 따라 자원(resource) 접근 여부 결정 가능
정보 교환
- JWT는 클라이언트와 서버 간, 또는 서버 간에 안전하게 데이터 전송 시 사용
- JWT는 서명된 형태로 데이터를 보호하므로 데이터 변조 불가
SSO(Single Sign-On)
- 한 번 로그인하면 여러 서비스에서 접근할 수 있도록 JWT 공유하여 인증 처리
- 사용자 경험 개선 및 관리 복잡성 감소 가능
API 보안
- RESTful API와 같은 무상태 애플리케이션에서 널리 사용
- 클라이언트는 요청 시마다 JWT를 포함시키고, 서버는 JWT를 검증하여 요청의 정당성 확인
Spring Security + JWT 동작 원리
Access Token
인증된 사용자가 특정 자원(resource)에 접근할 때 사용되는 토큰
- 클라이언트는 Access Token을 사용하여 인증된 사용자의 신원을 확인하고 서비스 또는 자원(resource)에 접근
- 유효기간이 지나면 만료(expired)
- 만료된 경우 새로운 Access Token을 얻기 위해 Refresh Token 사용
Refresh Token
Access Token의 갱신을 위해 사용되는 토큰
- 일반적으로 Access Token과 함께 발급
- Access Token이 만료되면 Refresh Token을 사용하여 새로운 Access Token 발급
- 사용자가 지속적으로 인증 상태를 유지할 수 있도록 도와줌
(매번 로그인을 다시 하지 않아도 OK)
- 보안 상의 이유로 Access Token보다 긴 유효기간을 가짐
로그인
-
클라이언트에서 ID/PW 가지고 서버로 로그인 요청
-
서버에서 검증 과정 거쳐서 해당 유저가 존재하면 Access Token + Refresh Token 발급
→ Refresh Token 같이 발급하는 이유: Refresh Token이 있어야 재발급 요청 처리가능!
-
클라이언트는 요청 헤더에 2번에서 발급받은 Access Token 포함하여 API 요청
토큰 갱신
- 클라이언트 Access Token 요청 보냈으나 유효시간 만료
- Refresh Token을 이용하여 Access Token 재발급 요청
- 클라이언트는 요청 헤더에 재발급받은 Access Token을 포함하여 API를 다시 요청
⇒ 위 과정은 사용자 모르게 자동으로 처리되어야 함(axios 인터셉터에서 처리)
사용자 인증

JwtAuthenticationFilter
- 모든 요청에 대해서 헤더에 토큰이 있는지 검사
- 토큰의 유효성 검사
- 토큰이 유효하면 SecurityContextHolder에 사용자 로그인 정보 전달
- 다음 필터로 이동
- SecurityConfig에서 필터 추가
SecurityConfig → 필터 순서와 form 로그인 사용 비활성화 설정
- OncePerRequestFilter 상속
- 컨트롤러가 forward 했을 때 필터 다시 거치게 되면서 이미 보안필터 통과했는데 또 통과하게 됨 → 요청 당 한 번만 필터 동작하도록 설정
인증 예외 처리

토큰 처리시 예외 발생 가능
- 여러 예외 중 토큰 만기 예외인 경우 401 Error로 리턴
- 그 외의 예외는 일반 예외 메시지로 처리
이번 주 수업에 대해서
Security의 경우 그림만 보면 어렵게 느껴질 수 있지만 하나씩 차근차근 살펴보니 그렇게 어려운 내용은 아니라고 느꼈다!
다만 Spring Security Chain 진도 나갈 때 병원 검진으로 인해서 수업에 참여하지 못해서 정리가 보다 힘들었던 것 같다. 시간 부족으로 실제 코드보다 개념 위주 정리여서 개인적으로 아쉽다. 가능하다면 코드부분도 추가하고 싶다!
