[Spring] Spring Security 기본 개념(JWT / OAuth 2.0 / 동작방식 / 구성요소)
이번엔 Spring Security의 기본 개념에 대해 공부했다.
1. JWT(Jason Web Token)
유저 인증, 식별하기 위한 토큰 기반의 인증에 쓰인다.
- 구조
- 헤더(Header)
- 타입(Type) : 항상 JWT
- 알고리즘(alg)
- 페이로드 (Payload) : 사용자 정보 담김
- 서명 (Verify Signature)
- 동작 방식
- 클라이언트 측에서부터 서버 측으로 JWT 받음
- 서버 측의 비밀 값과 JWT의 헤더, 페이로드를 alg에 넣고 서명값과 같은지 확인
- 같다면 유저에 인가한다.
- 특징
- 시간에 따라 상태 값이 달라지지 않음(Stateless)
- 서버가 통제하지 않아 여러 사용자가 같은 JWT로 여러 요청 보내도 추적 불가능
- 종류
- Access Token : 인가받았을 때 쓰는 수명이 짧은 토큰
- Refresh Token : Access Token을 재발급받을 때 쓰는 수명이 긴 토큰(DB에 저장, 관리)
2. OAuth
사용자들이 특정 사이트에 직접 회원가입 및 비밀번호를 제공하지 않고 접근하기 위해 권한을 부여받는 수단으로 사용되는 방식
✅회원 관리 기능(회원 가입, 로그인, 비밀번호 찾기 등) 구현 시 신경 써야 할 부분들을 대신 개발, 관리 편함
- 인증 Authentication : 해당 사용자가 본인이 맞는지 판단 (사용자 신원 확인)
- 인가 Authorization : 인증된 사용자가 요청한 자원에 권한이 있는지 판단 (사용자 권한 위임)
- OAuth 1.0
- 역할
- Resource Owner
- OAuth Client
- OAuth Server
- 동작방식
- 1. Client에서 Server로 요청
- 2. 인증, 인가 처리
- 3. Client에서 Resource Owner에게 전달(인증)
- 4. Server에서 Resource Owner 확인받음(인가)
- 5. Server에서 Client로 접근 = JWT 발급
- 문제점
- Scope 개념 없음(Scope : 유저 토큰(JWT)을 통해 데이터를 얻어오는 범위)
- 토큰의 유효 기간 문제
- 역할이 정확히 나눠져있지 않음
- Client 구현 복잡
- 제한적인 웹 환경(정적 리소스)
- OAuth 2.0
- 바뀐 점
- Scope 기능 추가 : 해당 토큰에 대한 접근 제한 범위 설정
- client 구현 복잡성 간소화 -> Bearer Token + TLS
- 토큰 탈취 문제 개선 -> Access Token + Refresh Token
- 동적 리소스 웹 환경까지 확장
3. Spring Security
https://docs.spring.io/spring-security/reference/index.html
Spring에서 필터를 사용해 유저 인증 / 인가 등 보안 기능을 쉽게 하도록 제공하는 프레임 워크
✅ 모든 요청이 인증되어야 자원에 접근 가능해짐 (메서드 단위 권한 부여)
- 주요 제공 기능
- 애플리케이션과의 모든 상호작용을 위해 인증된 유저 요청
- 기본 로그인 폼 제공
- BCrypt로 저장된 비밀번호 보호
- CSRF 공격 방지
- 구조
> FilterChain : Servlet 컨테이너가 관리하는 ApplicationFilterChain( =/= SecurityFilterChain)
1. 클라이언트가 애플리케이션에 요청을 보냄
2. 컨테이너는 Filter 인스턴스와 Servlet(요처 URI 기반의 HttpServletRequest를 진행)를 포함한 FilterChain 생성
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
// do something before th rest of the application
chain.doFilter(request, response);
// invoke the rest of the application
// do something after the rest of the application
}
> DelegatingFilterProxy : Servlet 컨테이너의 라이프사이클과 스프링 ApplicationContext 사이의 연결을 허용하는 필터 구현체
(= Servlet 컨테이너와 Spring IOC 컨테이너 사이의 연결해 주는 역할)
> FilterChainProxy : SecurityFilterChain을 통해 많은 필터 인스턴스를 위임하게 해주는 특별한 필터
> SecurityFilterChain : FilterChainProxy에 의해 스프링 시큐리티 필터 인스턴스가 현재 요청을 호출하도록 함
SecurityFilterChain 여러 개 사용하는 경우
- 요처 처리 구성 요소
HTTP 요청이 서버에 들어오면 DelegatingFilterProxy를 통해 Spring Security Filter Chain이 생성된다.
이는 요청 단위의 보안을 제공하는데 이 작업의 요청이 어떻게 처리되어야 하는지 결정된다.
모든 필터를 통과하면 각 요청은 등록된 Controller로 도달한다.
이후 서비스단으로 가기 전에 스프링 시큐리티는 AOP를 통해 메서드 단위로 권한 부여를 수행한다.
- 핵심 개념
- Authentication : 인증된 사용자 객체 (현재 로그인된 사용자)
- GrantedAuthority : 사용자에게 부여된 권한
- SecurityContext : Authentication 객체를 캡슐화하는 인스턴스
- SecurityContextHolder : 권한 부여를 수행할 때 사용
Maven
<dependency>
<groupId>org.springframwork.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Gradle
dependencies{
compileOnly "org.springframework.boot:spring-boot-starter-security"
}
의존성 추가 후 서버를 실행하면 이렇게 자동으로 security password가 생성된다.
그리고 기본 포트로 접속하면 로그인 창이 뜬다. 이는 스프링 시큐리티로 인해 그 어떤 요청을 하든 인증을 해야 접근할 수 있게 된 것이다.
아이디는 user, 비밀번호는 생성한 대로 적어 sign in을 해줘야 내가 설정한 대로 요청-응답이 된다.
application.properties에서 내가 직접 스프링 시큐리티 이름과 비밀번호를 설정해 줄 수도 있다.
spring:
security:
user:
name: user
password: 1234
- 세션
인증된 사용자에게 스프링 시큐리티는 세션 ID를 발급하고 이를 통해 관리한다.
- 스프링 시큐리티 설정
: WebSecurityConfigurerAdapter 상속해 커스텀 설정
✅ 커스텀 된 FilterChainProxy가 추가되는 것
OAuth 로그인 관련 설정도 여기서 해준다.