Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 217 additions & 0 deletions keyword/chapter10/keyword.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
- **Spring Security**


- 스프링에 기반해서 인증과 인가를 담당하는 보안 프레임워크이다

- 스프링 MVC와 분리되어 있어 보안과 관련된 부분과 비즈니스 핵심 로직을 구분해서 사용할 수 있다

흐름

1. 사용자가 로그인과 관련된 정보를 가지고 인증 요청을 한다

2. 유저 자격을 바탕으로 인증 토큰을 생성한다
1. AuthenticationFilter이 요청을 가져가고 이를 통해 UsernamePasswordAuthenticationToken 의 인증용 토큰 생성

3. Filter 를 이용해서 인증 토큰을 AuthenticationManager로 위임한다

4. AuthenticationProvider 의 목록들을 조회하며 인증을 시도한다

5. 실제 데이터베이스에서 사용자 인증 정보를 가져오는 UserDetailsService에 사용자 정보를 넘겨준다

6. 받아온 사용자 정보를 통해 UserDetails 객체를 생성한다

7. 사용자의 정보를 비교한다

8. 인증이 끝나면 사용자 정보를 담은 Authentication 객체를 반환한다

9. 끝

주요 컴포넌트

- SecurityContextHolder
- 가장 밖에 있는 컨테이너
- 현재 실행중인 스레드의 SecurityContext를 보관하는 곳
- static 클래스이다
- 어디에서든 현재 로그인 사용자 정보에 접근 할 수 있다

- SecurtiyContext
- 현재 요청에 대한 보안 상태를 담아둔다
- 인증이 성공할 경우 생성

- Authentication
- 해당 사용자가 누구고 어떤 권한을 가졌는지를 표현한다




- **인증(Authentication)과 인가(Authorization)**


인증

- 해당 사용자가 본인이 맞는지 확인하는 과정
- 요청 초기 필터 단계에서 일어난다
- 인증이 성공할시
- 사용자에 대한 정보가 담긴 Authentication 객체가 생성된다
- 인증이 실패한 경우
- 401 답변이 주로 반환된다

인가

- 해당 사용자가 특정 리소스에 접근이 가능한지를 판단
- 당연하게도 인증 이후에 인가가 수행된다
- 인가가 성공한 경우
- 해당 리소스를 볼 수 있게 된다
- 인가에 실패한 경우
- 403 응답을 반환한다
- 인가의 종류
- url 단위
- /admin/**
- 메서드 단위
- @PreAuthorize




- **세션과 토큰**


- 세션
- 서버가 사용자의 로그인 상태를 기억한다
- 로그인에 성공한 경우..
- 서버가 해당 사용자에 대한 세션을 생성
- 이를 서버 메모리나 세션 저장소에 보관한다

- 클라이언트
- 세션을 식별하는 세션 ID를 쿠키로 가져간다
- 모든 요청에 세션 ID 를 제공하고 서버는 이를 확인한다

- 대체적으로 서버의 부담이 보다 큰 구조이다

- 세션 공유 문제가 발생할 수도 있다
- 사용자 세션이 특정 서버에만 존재해 다른 서버에서는 해당 사용자의 로그인 상태를 알 수 없는 문제

- 구현이 토큰 방식보다는 간편하고 직관적이다



세션기반 흐름

1. 사용자가 아이디와 비번을 전송하다
2. 서버에서 검증한다
3. 서버가 세션을 만든다
4. 세션 ID를 만든다
5. 이를 쿠키로 클라이언트에게 준다

이후…

1. 클라이언트가 쿠키에 세션 ID 도 같이 준다
2. 서버가 세션 저장소에서 맞는지 조회한다
3. 맞는 경우 해당 요청을 처리한다


세션 공유 문제

- 사용자 세션이 특정 서버에만 존재해 다른 서버에서는 해당 사용자의 로그인 상태를 알 수 없는 문제

- 상황 예시
- 클라이언트, 로드 밸런서, 서버 A , 서버 B 가 있다고 가정

1. 사용자가 로그인을 할때 서버 A로 요청 전달
2. 서버A가 세션을 생성한다
3. 이후 요청이 서버 B 로 들어간다
4. 서버B 는 해당 세션에 대해서 알지 못한다
5. 따라서 인증이 실패하게 된다



- 해결 방법
1. Sticky Session
1. 같은 사용자를 항상 같은 서버에 보낸다
- 단점
- 특정 서버에 장애가 나면 해당 세션에 대한 자료가 사라질 수 있다
- 확장성이 낮아진다

1. 세션 클러스팅
1. 세션을 공용 저장소에 둔다
- 단점
- 인프라가 복잡해진다
- 비용이 증가한다

→ 이로 인해 토큰 방식이 나타났다




- 토큰 기반 인증
- 세션과 달리 서버가 로그인 상태를 기억하지 않는다
- 사용자가 인증에 성공하면…
- 서버가 사용자 정보를 참고해 토큰을 만들고 클라이언트에게 전달한다
- 해당 토큰은 서명이나 암호화 정보를 포함한다
- 클라이언트는 다음 부터 이 토큰을 서버로 전달한다
- 장점
- 서버가 사용자의 상태를 저장하지 않는 무상태성을 가진다
- 서버가 세션 공유 문제가 발생하지 않는다
- 수평확장이 보다 간편하다
- 단점
- 구현이 세션 기반 보다는 복잡하다
- 토큰의 만료기간 까지 해당 토큰이 유효하여 권한회수에 즉각적으로 대처하기 어렵다



- **액세스 토큰(Access Token)과 리프레시 토큰(Refresh Token)**


- 토큰 기반으로 구현을 하게 되면 토큰 탈취가 일어 날 수 있다!!
- 토큰이 한번 유출되면 해당 토큰이 만료되기 전까지는 계속 사용가능 하다

이를 해결하는 방법은 액세스 토큰과 리프레시 토큰을 사용하는것이다


- 엑세스 토큰
- 실제 api를 요청할 때 사용하는 토큰이다
- 만료 시간이 짧은 편이다
- 탈취될 가능성이 비교적 높다
- 만료시간이 짧은 이유

- 리프레시 토큰
- 엑세스 토큰이 만료되면 새로운 엑세스 토큰을 받기 위한 토큰이다
- api 요청에서는 사용되지 않는다
- 토큰 재발급 요청에만 쓰인다
- 만료시간이 길다
- 외부레 비교적 노출이 적다
- 탈취될 가능성이 낮다



사용 흐름

1. 로그인
1. 사용자가 로그인 시도한다
2. 서버에서 인증 완료
3. 엑세스 토큰과 리프레시 토큰을 만든다
4. 이를 클라이언트에게 준다

1. api 요청이 온다
1. 클라이언트는 여기서 엑세스 토큰을 같이 준다
2. 서버가 이를 확인한다
3. 검증 완료시 요청을 처리한다

1. 엑시스 토큰이 만료된 경우
1. 서버가 “야 엑세스 토큰 만료다!!!” 알려준다
2. 클라이언트가 이를 받고 리프레시 토큰을 서버에게 준다
3. 서버는 이 리프레시 토큰이 맞는지 검증한다
4. 맞다면 새로운 엑세스 토큰을 발급해서 준다


여기서 리프레시 토큰은 서버에 저장한다

원인

- 강제 로그아웃 구현시 필요
- 탈취 의심시 차단
- 토큰 재사용 가능성을 방지

엑세스 토큰은 이와 달리 서버에 저장하지 않는다

- 무상태성을 유지한다