이번에 채용 프로세스 중 과제 전형을 진행하면서, Spring security 6 버전을 이용해서 회원 기능을 구현하게 되었다.
Spring security를 직접 다뤄보는 것은 처음이라 인프런에서 강의도 찾아보고, 공식 문서도 참고하면서 80%정도 완성하였다. (20%는 시간 관계상 마무리 하지 못했다.)
Spring security의 Filter, Manager, Provider 소스 코드들을 직접 보면서 구현해서 이 Spring security 6으로 회원 기능 구현하기 시리즈로 이 경험을 기록
해보고자 한다.
이번 시리즈에서는 먼저 관련된 배경 지식을 알아보고자 한다.
그동안 대략적으로 알고 있었던 인증(AuthN, Authentication), 인가(AuthZ, Authorization) 라는 단어는 무엇을 말하는 것 일까?
인증(認證, authentication)은 참이라는 근거가 있는 무언가를 확인하거나 확증하는 행위이다. 객체를 인증하는 것은 이에 대한 출처를 확인하는 것을 뜻하는 반면, 사람을 인증하는 것은 사람들의 신분을 구성하는 것을 말한다. 인증은 하나 이상의 인증 요인에 따라 달라질 수 있다.
출처 : 위키피디아
위키백과엔 컴퓨터 분야에서의 인증 수단을 3가지로 분류하고 있다.
Spring security에서는 AbstractAuthenticationProcessingFilter 추상 클래스를 상속받은OAuth2LoginAuthenticationFilter, UsernamePasswordAuthenticationFilter 가 이를 담당한다.
Authorization or authorisation (see spelling differences) is the function of specifying rights/privileges for accessing resources, which is related to general information security and computer security, and to IAM (Identity and Access Management) in particular. More formally, "to authorize" is to define an access policy during the configuration of systems and user accounts. - 출처 : 위키피디아
번역하면 리소스에 액세스할 수 있는 접근 권한을 부여하는 것을 말한다. 사용자가 시스템 내에서 할 수 있는 행동을 결정한다. 접근 권한 부여 방법에는 3가지가 있다고 한다.
Spring security에서는 AuthorizationFilter가 이를 담당한다.
OAuth 2.0 인증 프레임워크는 리소스 소유자와 HTTP 서비스 간의 승인 상호 작용을 조율하여 리소스 소유자를 대신하여 타사 애플리케이션이 HTTP 서비스에 대한 제한된 액세스 권한을 얻거나 타사 애플리케이션이 자체적으로 액세스 권한을 얻을 수 있도록 허용합니다. - 출처 RFC 6749
OAuth 이전에는 제 3의 서비스에 저장된 정보를 갖고오기 위해서는 사용자에게 아이디, 비밀번호 등록을 유도하고 직접 인증해서 정보를 가져오는 방식을 사용했었다.
여러 스마트 스토어에 등록된 내 상품의 Q&A를 한번에 관리해주는 서비스 A가 있다고 가정해보자. 사용자는 네이버, 쿠팡, 11번가 등 직접 로그인 하지 않아도 서비스 A에서 미답변 Q&A와 답변달기를 할 수 있다.
서비스 A는 이 기능을 제공하기 위해 사용자로부터 네이버, 쿠팡, 11번가의 아이디와 비밀번호를 제공받아 이를 통해 데이터에 접근해왔다.
OAuth는 최종 사용자(사용자)와 클라이언트를 분리하였다. 위 예시에서는 서비스 A가 클라이언트가 되고, 리소스 서버는 스마트 스토어가 된다.
리소스 서버는 클라이언트에게 제한된 자원에 접근할 수 있는 권한을 부여한다. 최종 사용자가 가진 권한과는 다른 권한이다.
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
Figure 1: Abstract Protocol Flow
(A) 클라이언트가 리소스 소유자에게 인증을 요청.
(B) 리소스 소유자가 클라이언트에게 접근 권한을 제공.
(C) 클라이언트가 인증 서버에 접근 권한을 제시.
(D) 인증 서버가 클라이언트에게 액세스 토큰을 발급.
(E) 클라이언트가 리소스 서버에 액세스 토큰을 제시.
(F) 리소스 서버가 보호된 리소스를 클라이언트에게 제공.
RFC 6749에서 설명하는 프로토콜 흐름이다.
이를 통해 서비스 A는 사용자의 스마트 스토어의 인증 정보를 보관하지 않아도 되게 되었다.
RFC 6749
As background, the OAuth 2.0 Authorization Framework and OAuth 2.0 Bearer Token Usage specifications provide a general framework for third-party applications to obtain and use limited access to HTTP resources. They define mechanisms to obtain and use Access Tokens to access resources but do not define standard methods to provide identity information. Notably, without profiling OAuth 2.0, it is incapable of providing information about the authentication of an End-User. Readers are expected to be familiar with these specifications.
OpenID Connect implements authentication as an extension to the OAuth 2.0 authorization process.
출처 : OpenID Connect Core 1.0 incorporating errata set 2
번역하면 OAuth 2.0 프레임워크는 리소스에 액세스 하기 위해 토큰을 획득하고 사용하는 메커니즘을 정의하지만 인증 정보를 제공하기 위한 방법은 정의하고 있지 않는다. OIDC는 OAuth2.0 인가 프로세스 기반으로 인증 기능을 구현한다.
+--------+ +--------+
| | | |
| |---------(1) AuthN Request-------->| |
| | | |
| | +--------+ | |
| | | | | |
| | | End- |<--(2) AuthN & AuthZ-->| |
| | | User | | |
| RP | | | | OP |
| | +--------+ | |
| | | |
| |<--------(3) AuthN Response--------| |
| | | |
| |---------(4) UserInfo Request----->| |
| | | |
| |<--------(5) UserInfo Response-----| |
| | | |
+--------+ +--------+
OIDC는 JWT 타입의 Id token을 발급해준다. Id token의 클레임 영역에는 사용자 정보가 담겨있다. 제 3의 서비스는 이 Id token의 값으로 사용자의 정보를 확인할 수 있다.
JWT 토큰 기반 인증 방식을 구현한다면 대개 access token으로 서버에 사용자 정보를 요청해서 클라이언트에 저장하거나 access token의 클레임에 사용자 식별 정보를 담아서 사용했을 것 이다.
Access token의 클레임에 대해서 표준으로 정의된 것은 없어서 잘못된 방법은 아니다. 하지만 목적성이 다르다.
Access token은 리소스의 인가 여부를 판단하는 토큰이며, API 요청할 때 포함해서 보낸다. 클라이언트는 access token을 복호화해서 내부 정보를 파악하지 않는다. 출입 카드에 사용자 식별자가 부여된 느낌이다.
Id token은 사용자의 정보를 담고 있는 토큰이며 클라이언트는 이 토큰을 복호화하고 검증을 해 타인이 위조한 정보가 아님을 검증한다. 이 토큰은 API 요청에 포함해서 보내는 목적은 아니고 사용자의 정보를 제공 것에 초점을 두었다.
예시로 카카오 소셜 로그인을 OIDC를 통해 구현했다고 해보자.
우리는 마지막 단계에 /user/me API에 access token을 담아서 사용자 정보를 요청한다. OIDC 프로토콜을 사용했다면 id token의 클레임에 접근을 허가받은 사용자 정보가 담겨있다. 카카오 API 문서
이번 시리즈에서는 회원 기능을 이해하기 위해 공부했던 내용을 간략하게 정리해보았다.
Id token과 OIDC는 이해하는데 좀 시간이 걸렸었다. Chat GPT와 Perplexity의 도움을 받아서 access token과 id token의 차이는 목적성이라는 것을 이해하고 나니 조금 그림이 그려지는 느낌이다.
포스팅 된 내용에 대한 문의, 지적은 정중하게 환영합니다. 소중한 시간을 내어 피드백 주셔서 감사드립니다.