forDevLife
[TIL]9/15(5일차) - WEB2 : OAuth 2.0 본문
생활코딩 OAuth 2.0 정리
1. OAuth 2.0
accessToken을 통해 SERVICE(google, facebook 등)에 접근할 수 있는 핵심이다.
mine : 우리가 만든 서비스 (Client)
their : 로그인 기능을 지원해주는 대형 service(우리가 제어하고자 하는 자원 - user의 자원을 가진 서버 = 리소스 서버)
user : their이라는 service에 가입이 되어있는 회원이며, 우리가 만든 서비스에 접근하고자 함 (= 리소스 owner)
+ Authorization server : 인증을 처리하는 서버, 리소스 서버와 합쳐 설명된다.
2. OAuth 2.0 등록
Client는 리소스 서버의 승인을 사전에 받아둬야 한다. 이를 register라고 하며, 이를 살펴본다.
Clinent ID : 우리의 서비스 ID
Client Secret : 우리 서비스의 비밀번호(외부에 절대로 노출되면 안된다.)
Authorized redirect URIs : 리소스 서버가 권한을 부여하는 과정에서 Authrization code를 전달한다. 권한이 부여된 redirect URI 여기로 요청을 다시 하라는 의미이다.
3. Resource Owner의 승인
등록(register)의 결과로, 리소스 서버는 client의 ID, secret, redirect URL을 알게 된다.
뒤에서 보겠지만 redirect URL에 해당하는 페이지를 준비하고 있어야 한다.
만약 Resource Server가 A, B, C, D의 기능을 가지고 있으며 Client가 B, C기능만 사용하고자 한다면 최소한의 기능에 대해서만 인증받는게 더 효율적이다.
- 리소스 오너(사용자)가 클라이언트(우리의 서비스) 사용 중, 리소스 서버의 기능을 사용하고자 할 때 클라이언트는 소셜 로그인 배너를 리소스 오너에게 보내고, 동의 여부를 묻는다.
- 이 때 배너는 https://resource.server ~와 같은 리소스 서버의 링크를 제공하면 된다.
구글 로그인 배너의 예시이다.
이러이러한 기능을 클라이언트에서 요청하고 있는데 허용할 것인가?를 리소스 오너에게 보내서 확인한다.
허용 후 resource server는 user_id 1은 scope b, c를 client에 허용하겠다는 정보를 저장한다.
리소스 오너의 허용으로, 리소스 서버에는 client id, secret, redirect URL, user id, scope가 저장되게 된다.
이 의미는, 클라이언트 id 1에게 Resource owner(user) 1번에 대한 정보를 scope b, c에 한정하여 제공할 수 있다는 의미이다.
오너의 허용을 받았으니, 이제 리소스 서버의 허용을 받을 차례가 된다.
리소스 서버는 이들을 기반으로, authorization code = 3을 만들고 클라이언트의 리다이렉션 주소와 함께 Location 헤더를 응답한다.
리소스 오너는 해당 URL로 리다이렉트 된다.
클라이언트는 이 시점에 authorization code를 알 수 있게된다.
이제 클라이언트는 클라이언트 secret, authorization code를 리소스 서버에 전달하게 되고,
리소스 서버는 이를 기반으로 Access Token을 발급할 것이다.
authorization code를 통해 인증을 마친 상황이며, 더 이상 필요하지 않으니 지워진다.
이때 Resource Server는 access Token을 발급합니다. Client에게 accessToken 값을 응답해 Client는 accessToken을 알게 된다. Client가 acessToken이 4라는 것을 통해 Resource Server에 접근하면, Resource Server는 accessToken을 보고 그 accessToken을 갖고 있는 사용자에게 허용하게 된다. 이를 통해 클라이언트에서 리소스 오너의 정보를 저장할 수도 있으며, 리소스 서버가 제공하는 API를 사용할 수도 있다.
+ Refresh Token
Access Token은 수명이 있으며, 수명이 끝나면 API에 접속했을 때 API가 데이터를 주지 않는다. 다시 발급받아야 할 때 사용되는 것이 Refresh Token이다. 보통 Access Token이 발급될 때 Refresh Token이 동시에 발급된다.. Access Token이 만료되면 가지고 있었던 Refresh Token을 Resource Server에게 넘겨주고 다시 Access Token을 받게된다.
요약
1. 개발자 입장
Client가 어떤 서비스를 제공하려고 할 때 기능을 가지고 있는 Resource Server(예를 들어 Facebook 등)의 해당 기능을 이용하려는 것입니다. 이를 통해 Client는 사용자인 Resource Owner가 API를 사용할 수 있도록 합니다.
그 API를 이용하려면 먼저 Resource Sever로 등록이 필요합니다. 등록을 통해서 Client는 Client ID와 Client Secret을 받게 됩니다. 동시에 redirect URL도 받습니다.
2. 사용자 입장
앞서 내용을 남긴 개발자 입장은 사용자 입장에서 신경쓰지 않습니다. 개발자 입장은 단지 서비스를 제공하기 위해서 다른 누군가의 API를 사용할 것이고, 이를 위해서 등록을 했을 뿐입니다.
먼저 누군가가 Client가 제공하는 서비스를 이용하고 싶어한다고 가정하겠습니다. Client는 서비스 이용을 위해 Client에 접속하려고 하는데, 이와 같은 움직임이 발생하면 사용자는 선택권을 갖게 됩니다. 만약 사용자가 Resource Server에 로그인되어있지 않다면, 로그인을 요구한 후 사용자가 이용하려는 Client의 서비스가 Resource Server에서 제공하는 기능에 접근하려고함을 알려줍니다. 이때 사용자가 수락하면 사용자는 결국 Resource Server에 있는 API를 Client를 통해 사용하겠다는 의미가 됩니다.
3. Resource Server 입장
앞서 개발자 입장에 내용을 적지는 않았지만, 개발자나 Resource Server는 모두 사용자의 정보에 대한 보안에 신경쓰려합니다. Resource Server는 마지막에 Access Token을 발급할 것인데, 이 Access Token이 OAuth의 핵심임을 알고 있습니다. 사용자의 ID와 Password를 직접 다루지 않게 해서 보안에 신경쓴다는 의미입니다.
다시 돌아와서 순서대로 살펴보면, 사용자는 궁극적으로 Resource Server의 API를 사용하려는 것이고 그렇게 하기까지 보안에 신경쓴 움직임이 이뤄져야 합니다. 먼저 Resource Server는 Authorization Code라는 임시 비밀번호를 생성하고 사용자의 웹 브라우저에게 Redirection할 것을 명령합니다. Authorization Code를 갖고 Client에게 다시 연결하면, Client는 Resource Server에게 직접 접속을 시도합니다.
Resource Server는 지금까지 움직임에서 많은 정보를 알고 있습니다. Client로부터 넘겨받은 Authorization Code, Client Secret 두 가지를 가지고 자신이 갖고 있었던 것과 일치하는지 확인합니다. Authorization Code, Redirect URL, Cliend ID, Client Secret 모두가 일치하면 다음 단계로 넘어갑니다.
여기서 잠깐 Authorization Code의 흐름을 다시 살펴보면 아래와 같습니다.
- Authorization Code는 Resource Server로부터 생성되어 사용자에게 전달합니다.
- 동시에 Resource Server는 사용자의 웹 브라우저에게 Authorization Code를 갖고 Redirection할 것을 명령합니다.
- Client는 웹 브라우저로부터 받은 Authorization Code를 갖고 Resource Server에 접근합니다.
- 마지막으로 Resource Server는 그동안 전달되었던 정보를 가지고 일치하는지 여부를 판단합니다.
Authorization Code의 사용은 끝나고 이제 소멸됩니다. 그리고 Resource Server는 Client에게 Access Token을 보내 Access Token을 가지고 있는 사용자가 API를 이용할 수 있도록 허용합니다.
스프링 시큐리티 적용 (feat. kakao)
1. Site domain & redirection url 등록(https://developers.kakao.com/console)
해당 URI는 브라우저에서 카카오 로그인 -> 권한 허용 후 클라이언트로 code를 쿼리 스트링으로 보내기 위한 주소이다.
카카오 서버에서 유저 로그인 / 권한 확인 후 브라우저에 Location 헤더로 전달하게 된다.
다음과 같은 형식으로 redirect : http://localhost:8080/login/oauth2/code/kakao?code={서버에서 발급하는 코드}
2. 동의 항목 선택
3. application.properties or yml 작성
카카오는 "CommonOAuth2Provider"에 등록되어있지 않으므로 직접 등록해줘야 한다.
* CommonOAuto2Provider에는 구글, 깃허브 등 몇 가지만 등록되어 있다. 이는 spring-autoconfigure-metadata.properties에 들어 있어, 자동으로 등록된다.
- registration : client쪽의 정보를 입력. 발급받은 id, redirect-uri 등을 형식에 맞게 입력한다. id만 작성하고 위와 동일하게 작성
- provider : Autho Server인 카카오쪽의 uri를 작성한다.
-. 배너에서 누르는 uri(로그인 버튼 누르면 연결) : /oauth2/authorization/kakao로 이동해서 로그인 화면을 띄워준다.
-. autho-uri : 동의 하게되면 해당 uri로 아래와 같은 요청이간다.
GET /oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code HTTP/1.1
Host: kauth.kakao.com
-. token-uri : 인가 코드를 받은 뒤, 인가 코드로 액세스 토큰과 리프레시 토큰을 발급받는 API이다.
인가 코드 받기만으로는 카카오 로그인이 완료되지 않으며, 토큰 받기까지 마쳐야 카카오 로그인을 정상적으로 완료할 수 있다.
필수 파라미터 값들을 담아 POST로 요청되며, 요청 성공 시, 응답은 JSON 객체로 Redirect URI에 전달되며 두 가지 종류의 토큰 값과 타입, 초 단위로 된 만료 시간을 포함하고 있다.
사용자가 로그인에 성공하면 발급되는 액세스 토큰(Access Token)과 리프레시 토큰(Refresh Token)은 각각 역할과 유효기간은 다르다.
실제 사용자 인증을 맡는 액세스 토큰은 비교적 짧은 만료 시간을 가진다. 하지만 유효한 리프레시 토큰이 있다면, 사용자가 다시 로그인했을 때 리프레시 토큰으로 액세스 토큰을 다시 발급받을 수 있다.
-. user-info-uri : 발급받은 엑세스 토큰을 헤더에 넣어, 유저 정보를 받아올 수 있다.
-. user-name-attribute : 다른 소셜에서 사용되며, 호환성을 위해 존재한다. 그냥 id라고 써준다.
4. SecurityConfig 작성
.antMatchers를 통해 url마다 권한을 부여한 후, userServce인 customOAuto2UserService로 사용자 정보를 가져오기 위해 넘어감.
5. CustomOAuth2UserService
위 코드의 분석에 앞서, OAuth2UserRequest를 분석해보면 다음과 같은 인스턴스를 가지고 있다.
- ClientRegistration : yml에 작성한 클라이언트 id, secret, redirectURL 형식의 정보, Provider(소셜 서비스) detail 정보를 담는다.
- OAuth2AccessToken : token type, scopes를 가진 토큰 관련 클래스
- additionalParameters : 추가 파라미터 정보
코드를 분석하면,,
1) DefaultOAuth2UserService를 생성 & delegate(대리자)를 만든다.
2) delegate.loadUser(userRequest)를 통해 oAuth2User가 반환된다. 여기에는 응답된 user 정보가 들어있다.
3) 위에서 생성된 oAuth2User의 attribute를 get하여 새로운 OAuthAttributes DTO를 생성한다. parsing을 통해 사용자 데이터에서 필요한 항목만 뽑아 DTO로 만드는 과정으로 생각하면 된다.
- attribute를 기반으로 userRepository에 user를 add / update할 수 있다.
- 이후 user를 serialize할 수 있도록 만든 DTO인 SessionUser를 생성해서 httpSession에 setAttribute한다. user를 serialize하면 위험하므로, 꼭 DTO를 생성해서 사용해야한다.
- httpSession.setAttribute된 user는 어디에서 쓰이나?
LoginUserArgumentResolver에서 @LoginUser 어노테이션과 SessionUser를 인자로 가진 파라미터를 대상으로 Argument를 찾아서 사용할 수 있도록 해준다. 아래 참고
따라서 controller의 파라미터로 user를 받아올 수 있으며, user가 null이 아닐 경우 userName을 담아 인덱스 html로 보내어 브라우저에 userName을 띄울 수 있게 된다.
'각종 회고' 카테고리의 다른 글
[TIL]11/10 - 웹소켓 & 스프링 (0) | 2021.11.10 |
---|---|
[TIL]9/15 - AOP (0) | 2021.09.29 |
[TIL]9/14(4일차) - Spring Boot Security + OAuth (0) | 2021.09.14 |
[TIL]9/13(3일차) (0) | 2021.09.13 |
[TIL] 9/10 (2일차) (0) | 2021.09.10 |