PART 1. 인증과 인가 💳
Spring Security는 '인증과 인가'를 제공하는 데에 초점을 맞춘 프레임 워크입니다.
이때, 인증과 인가가 비슷한 단어처럼 생겼지만 정확한 차이점이 있습니다. 따라서, 이번 기회에 더 자세하게 살펴보도록 하겠습니다.
1-1. 인증 (Authentication)
- (식별 가능한 정보로) 서비스에 등록된 유저의 신원을 입증하는 과정
- 보호된 리소스에 접근하는 것을 허용하기 전에 등록된 유저의 신원을 입증하는 과정
1-2. 인가 (Authorization)
- 인증된 사용자에 대한 자원 접근 권한 확인하는 과정
- 요청된 리소스에 접근할 수 있는 권한이 있는 인증된 유저인지 입증하는 과정
☝️ 예시를 들면서, 인증과 인가에 대해서 이해해보겠습니다.
먼저, '소프트웨어 마에스트로'에서는 접수처에서 연수생의 얼굴과 이름을 확인하고 나서 연수생에게 출입증을 발급해줍니다. 그리고 나서, 해당 연수생은 발급받은 출입증을 사용해서 여러 회의실을 방문할 수 있지만 사무실은 사무국 직원들만 출입증으로 방문할 수 있습니다.
- 이때, 연수생의 얼굴과 이름을 확인하고 출입증을 발급받는 과정은 인증입니다. 그리고 출입증으로 여러 회의실을 방문하고 사무국에 못 들어 가는 과정은 인가라고 할 수 있습니다.
- 즉, 어느 사이트에 들어가서 글을 작성하기 위해서 로그인과 회원가입을 하는 과정은 인증이고 회원이 되면 글을 작성하는 과정에서 인가가 발생합니다.
- 여기에서 중요한 점은 인증은 인가보다 반드시 먼저 발생한다는 것입니다. 인증을 하고 나서 인가를 안 할 수는 있지만, 인가를 하기 위해서는 반드시 인증을 해야 합니다.
정리해보면, 인증과 인가는 "자원을 적절하고 유효한 사용자에게 전달 및 공개하기 위한 방법"입니다.
PART 2. HTTP의 Stateless(무상태성) 🙈
웹에서의 인증과 인가 방법에 대해서 살펴보기 전에, HTTP에 대해서 알아야 합니다. 왜냐하면, HTTP는 클라이언트와 서버가 소통하는 방식이며, 클라이언트가 서버에 저장된 정보를 보기 위해서는 소통을 해야 하기 때문입니다.
이때, HTTP에서 가장 중요한 점은 Stateless(무상태성)이라는 점입니다.
즉, 서버가 클라이언트의 이전 상태를 보존하지 않는다는 것을 의미합니다.
먼저, HTTP가 Stateful인 경우를 예시로 살펴보겠습니다.
승객 : "서울에서 전주로 가는 기차는 얼마인가요?"
직원 : 25,000원 입니다.
승객 : 2장 주세요.
직원 : 50,000원입니다.
(직원은 이전에 상황을 기억해서 1장당 25,000원이라는 것을 기억한다.)
그리고 HTTP가 Stateless인 경우를 예시로 살펴보겠습니다.
승객 : "서울에서 전주로 가는 기차는 얼마인가요?"
직원 : 25,000원 입니다.
승객 : 2장 주세요.
직원 : 뭘 2장 달라는 건가요??
(직원은 이전에 상황을 기억해서 1장당 25,000원이라는 것을 기억하지 못한다.)
즉, HTTP는 이전 상태를 기억하지 못하기 때문에 로그인하고 나서 다른 페이지로 가서 요청해도 서버는 해당 사용자가 누구인지 모릅니다. 따라서 클라이언트는 로그인한 정보를 세션이나 쿠키 등에 저장해놓아서 HTTP로 서버에게 요청시에 사용자의 정보를 알려줘야 합니다.
PART 3. 웹에서의 인증과 인가 방법 👩💻
- 인증하기 ➡️ Request Header
- 인증 유지하기 ➡️ Browser
- 안전하게 인증하기 ➡️ Server
- 효율적으로 인증하기 ➡️ Token
- 다른 채널을 통해서 인증하기 ➡️ OAuth
사용자는 (ID는 user, PW는 1234)일 때, 클라이언트에게http://user:1234@naver.com/login
로 요청하고 클라이언트는 ID와 PW를 파싱하여 Authorization의 임의 코드(abtuo909hgvbmi
)로 변환하여 서버에게 전송합니다.
이때, 문제점은 매번 인증을 해야 한다는 점입니다. 따라서, 이를 해결하기 위해서 브라우저의 쿠키, 세션, 로컬 스토리지에 저장합니다.
3-2. Browser를 활용하여 인증 유지하기
위의 문제점을 해결하기 위해서, 사용자가 입력한 정보들을 브라우저에 ID와 PW를 저장합니다. 그러면 사용자가 계속 로그인(인증)하지 않아도 브라우저에서 서버에게 사용자의 정보를 알려줄 수 있습니다.
그러나, 해당 방법에도 문제점이 있습니다.
바로 바로 "해킹" 당하기 쉽다는 문제입니다. 😈
해커가 브라우저에서 해당 ID와 PW를 가져가면, 언제든지 로그인을 할 수 있습니다. 따라서 클라이언트쪽에서 정보를 저장하는 경우에는 패스워드와 같이 민감한 정보를 저장하지 않아야 합니다.
3-3. Session을 활용하여 안전하게 인증 유지하기
이번에도 위의 문제를 해결하기 위해서 인증을 안전하게 유지할 수 있습니다.
서버는 인증된 사용자에게 임의의 세션 ID를 발급해줍니다. 그리고 해당 세션 ID를 클라이언트의 저장소에 저장해놓습니다. 이때, 서버에서도 세션 ID를 저장하게 됩니다.
그러면, ID와 PW를 저장하는 것과 세션 ID를 저장하는 것과 어떠한 차이점이 있을까요?
- 세션은 세션의 만료기한을 정할 수 있습니다. 따라서 탈취된 세션을 서버에서 삭제하면 해당 세션 ID는 사용할 수 없습니다.
그러나, 세션을 사용하는 것에도 문제점이 있습니다.
- 로드 밸런서에 의해서 여러개의 서버로 나누어져 있으면, 세션 아이디를 발급한 서버가 아닌 다른 서버에 세션 아이디를 주게 되면 해당 세션 아이디는 없어서 에러가 발생합니다.
이러한 문제들을 해결하기 위해서 하나의 서버에 저장하지 않고 세션을 따로 저장하는 DB를 사용하여 여러 서버에서 해당 DB를 사용하게 됩니다. 그러나, 이러한 방법이 아니라 토큰을 사용하면 따로 DB를 두지 않고 인증을 할 수 있습니다.
3-4. JWT 활용하기
- 위의 문제를 해결하기 위해서, 서버에서 하나의 secret key를 가지고 사용자의 정보(이름, 만료시기, 권한 등)을 암호화하여 access token을 클라이언트에게 전달해주면 클라이언트의 저장소에 저장해놓습니다.
- 이때, 주의할 점은 secret key는 반드시 주의해서 절대로 다른 곳에 유출되면 안됩니다!! 그리고 사용자의 정보를 암호화할 때는 반드시 비밀번호와 같은 민감한 정보는 넣으면 안된다는 점을 명심해야 합니다.
- 그러면 서버는 DB에 접속하지 않고도 secret key를 사용하여 클라이언트에서 전송한 access token를 통해서 사용자의 정보를 얻을 수 있습니다!!
➡️ 서버를 접근하지 않고 사용자의 정보를 얻을 수 있어서 오버헤드를 최소화할 수 있습니다.
☝️그러나, 토큰 관리에도 유의해야 합니다. 따라서 access token을 만료 기한을 두고 해당 토큰을 갱신해줄 Refresh token도 발급해줍니다. 그리고 access token은 Refresh token보다 훨씬 짧은 만료기한을 가지고 있습니다.
3-5. OAuth2.0을 활용하여 인증하기
- OAuth2.0를 사용하면, 사용자는 기존의 회원가입되어 있는 소셜 계정을 통해서 손쉽게 회원가입을 할 수 있습니다. 그리고 개발자에게도 로그인 구현시 고려해야 할 점을 간단하게 바꿔줍니다.
예시) 구글 로그인, 페이스북 로그인, 깃헙 로그인 등을 사용하여 인증절차를 대신한다.
- 로그인시 보안, 비밀번호 찾기, 비밀번호 변경, 회원정보 변경, 회원가입 시 이메일 혹은 전화번호 인증을 구글, 페이스북, 네이버 등 여러 소셜 서버에서 신경써줍니다.
- 그러면, 소셜 서버에서는 왜 OAuth2.0를 제공해줄까요??
소셜 서버에서는 OAuth2.0를 제공해줌으로써, 자신의 서비스에서 벗어날 수 없도록 회원 유지를 늘려준다고 합니다.
💭 만약에 네이버 로그인으로 '지그재그'와 '당근마켓'등을 회원가입했다고 생각하면, 지그재그를 사용하기 위해서는 네이버를 탈퇴하면 안됩니다. 따라서, 네이버를 탈퇴하고 싶어도 기존에 사용하고 싶은 지그재그 등을 사용하지 못하므로 네이버를 탈퇴하지 못하게 됩니다.
- 이처럼, OAuth2.0는 사용자와 소셜 서비스, 개발자 모두에게 도움을 줄 수 있기 때문에 많이 사용되고 있는 추세입니다.
PART 4. 인증방법 선택하기
- 위의 인증 방법에 대해서 설명했는데, 이 중에서 JWT와 OAuth2.0을 사용하여 인증과 인가를 구현하려고 합니다.
OAuth2.0 이유 : 사용자와 개발자 입장에서 모두 편리하다.
- 이번에 진행하는 프로젝트는 빠르게 개발을 하고, 많은 사용자를 유입시키는 것이 목적이었습니다. 따라서, 이 두 가지의 조건을 만족하기 위해서는 사용자가 편리해야 하고, 여러 로그인 처리를 간편하게 해주는 OAuth2.0가 적합하다고 생각했습니다.
JWT 이유 : 확장성과 보안 및 접근성이 좋다.
- 세션과 JWT가 큰 차이는 없어보이지만, 가장 큰 차이점은 "JWT는 확장성이 더 크다"는 점입니다. 세션은 해당 ID를 서버에 저장하고, 서버가 확장되는 경우 해당 session id를 찾아야 하므로 힘듭니다. 그래서 session id를 저장하는 DB를 따로 둬야 합니다. 그러나, JWT는 secret key만 있으면 서버가 확장되어도 모든 서버에서 사용자의 정보를 알아낼 수 있습니다.
- 이때, 가장 헷갈렸던 점은 JWT에서 access token만 하면 세션보다 좋은 점을 알지만 refresh token을 발급하면 동일하게 token 관련 DB를 둬야 하므로 세션 ID를 DB에 넣는 방식의 세션과 큰 차이가 없는 것이 아닐까라는 생각을 했습니다. 그러나, JWT는 해당 token이 만료되었을 때만 DB에 접근하지만, 세션은 클라이언트가 서버에 API를 요청할 때마다 서버는 DB에 접근해야 합니다.
참고자료
참고 자료 1 : [10분 테코톡] 🎡토니의 인증과 인가
https://youtu.be/y0xMXlOAfss
참고 자료 2 : [10분 테코톡] 🐻작은곰의 Spring Security
https://www.youtube.com/watch?v=aEk-7RjBKwQ
참고 자료 3 : [10분 테코톡] 🤠루피의 인증과 인가
https://www.youtube.com/watch?v=JZgD8aPkHSc&t=638s
참고 자료 4
https://velog.io/@duarufp06/HTTP-Stateless-Connectionless-HTTP-메시지-개념
참고 자료 5
https://velog.io/@znftm97/JWT-Session-Cookie-비교-sphsi9yh