프로젝트를 진행하면서, 개의 중성화여부를 나타내는 컬럼을 추가해달라는 요청을 받았다.

true / false값을 저장하는 is_neutered필드를 추가하기로 했고, 찾아보니 MySQL에서는 boolean타입을 별도로 지원하지 않는다고 해서 TINYINT(1)타입으로 필드를 만들어줬다. 1은 true, 0은 false를 나타낼 수 있게끔.

 

 

// Dog entity
    @Column(columnDefinition = "TINYINT(1)", nullable = false)
    private boolean isNeutered;

 

그리고 강아지를 등록할 때 사용하는 dto들에도 마찬가지로 isNeutuerd라는 필드들을 추가해줬다.

 

@Getter
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class SignupRequestDto {
    private String email;
    private String name;
    private String dogName;
    private String dogType;
    private String dogGender;
    private boolean isNeutered;
    private int dogAge;
    
    // ... 생략

 

근데 이윽고 잠시 후, 문제가 생겼다. 실제 DB에 쌓이는 데이터들을 보는데, dog테이블의 is_neutered컬럼이 죄다 0만 저장되고 있던 거다.

 

참고로 프론트(플러터 단)에서는 요로코롬 true값을 담아서 request를 보내고 있던 상황이었음

 

      var res = await dio.post(
        "http://localhost:8080/api/auth/signup?provider=$oauth2Provider",
        data: {
          "email": email,
          "name": _userName,
          "dog_name": _dogName,
          "dog_type": _dogType,
          "dog_gender": _dogGender,
          "is_neutered": true,
          "dog_age": _dogAge,
        },
      );

 

뭘까 싶어서 컨트롤러에서 SignupRequestDto의 값을 찍어봤는데, 이럴 수가!! (ㅎㅎ..내가 봐도 오글거리는 말투다)

isNeutered필드 값이 false가 찍히고 있었다!

 

단순히 이 현상만 보자면 프론트에서는 true를 넘겼는데 백엔드에서 받고 보니까 false다..? 중간에 해커가 탈취해서 조작했나..? 오만가지 생각이 다 들었다.

 

하지만 구글링을 통해 비교적 쉽게 문제를 해결할 수 있었다. 바로 dto에서 boolean이 아니라 Boolean으로 타입을 변경해주는 것을 통해서!

 

 

일단 원인부터 정리하자면 다음과 같았다

 

  • 롬복(Lombok)은 @Getter나 @Setter를 붙이면 필드들에 대해 get뭐시기, set뭐시기같은 게터 & 세터 메소드들을 만들어준다.
  • 근데 만약 필드가 boolean타입이면서 is로 시작하는 필라면(ex: isExpired), getIsExpired나 setIsExpired같은 이름으로 메서드를 만들어주는게 아니라 isExpired, setExpired란 이름의 게터와 세터를 만들어준다

 

결국 내가 SignupRequestDto에 작성한 isNeutered라는 필드에 대한 게터가 getIsNeutered가 아닌 isNeutered로 작성된 것. 근데 이게 왜 문제가 되는가? 바로 컨트롤러에 있는 @RequestBody에서 문제가 된다.

 

    @PostMapping("/signup")
    public BaseResponse<JwtResponseDto> signup(@RequestParam("provider") String oauth2ProviderName,
                                               @RequestBody SignupRequestDto signupRequestDto) {
        Oauth2Provider oauth2Provider =
                Oauth2Provider.getOauth2ProviderByName(oauth2ProviderName);
        
        // ... 생략

 

@RequestBody가 어떻게 동작하길래 dto의 게터가 isNeutered인게 문제가 되는 걸까?

 

알다시피 이 놈은 client가 보낸 request의 json데이터를 Dto에 매핑해주는 역할(역직렬화)을 한다.

MappingJackson2HttpMessageConverter가 내부적으로 ObjectMapper를 활용해서 값을 dto에 바인딩해주는데, 이 때 dto에 게터들이 있어야 한다. 즉 get뭐시기라는 형식의 이름을 가진 메서드들이 있어야 하는데, 롬복선생께서 isNeutered의 게터를 getIsNeutered가 아니라 isNeutered로 만들어줘서 문제가 생기는 것. (적절한 게터를 찾게 되지 못하는 것이기 때문) 이때 boolean타입의 기본값은 false기 때문에 자동으로 false가 들어가져있던 거다.

 

즉 이를 해결하기 위해선 이리 볶든 저리 볶든 Dto클래스에 getIsNeutered라는 게터가 있으면 되는거다.

물론 내가 직접 게터를 dto클래스에 만들어주면 되나! 필드의 타입을 boolean이 아닌 Boolean으로 작성(즉 래퍼클래스를 쓰는 형태로 작성)하면, is라는 접두사로 시작하는 필드여도 get뭐시기 라는 형태로 만들어진다고 한다.

 

@Getter
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class SignupRequestDto {
    private String email;
    private String name;
    private String dogName;
    private String dogType;
    private String dogGender;
    private Boolean isNeutered;
    private int dogAge;
    
    // ... 생략

 

 


✅ 참고한 글들

 

https://jungguji.github.io/2020/12/31/RequestBody-Annotation-%EC%82%AC%EC%9A%A9-%EC%8B%9C-boolean-%EB%B3%80%EC%88%98-%EB%B0%94%EC%9D%B8%EB%94%A9-%EC%97%90%EB%9F%AC/

 

RequestBody Annotation 사용 시 boolean 변수 바인딩 에러

서론토이프로젝트 중 @Reqeust 어노테이션을 적용한 DTO에서 boolean 데이터를 제대로 전달 받지 못하는 문제가 발생하여 이를 정리한다. 문제 발생vue.js에서 넘어온 데이터를 @RequestBody 어노테이션을

jungguji.github.io

https://sedangdang.tistory.com/305

 

@RequestBody 공식문서를 읽어보자 + HttpMessageConverter

의역/오역이 있을 수 있고 잘못된 부분이 존재할 수 있습니다 요약 1. @RequestBody는 HttpMessageConverter가 HTTP Request Body 내의 데이터를 객체로 변환(역직렬화)하도록 시키는 애노테이션이다. 2. @Valid를

sedangdang.tistory.com

https://velog.io/@soyeon207/Spring-Lombok-%EC%82%AC%EC%9A%A9%EC%8B%9C-is-prefix-%EB%8A%94-%EC%99%9C-%EC%97%86%EC%96%B4%EC%A7%88%EA%B9%8C

 

Lombok 사용시 is prefix 는 왜 없어질까 ?

isNew 라는 신상품 여부를 나타내는 변수가 있었는데 response 로 DTO 내려줄 때 is 가 사라지고 new 라고만 계속 보여졌다.

velog.io

 

둘 다 DB에서 데이터를 삭제하는 방식에 관한 것이다. Hard delete는 물리 삭제라고도 부르고 Soft delete는 논리 삭제라고도 부르는데, 이름에서 유추할 수 있듯이 Hard delete는 물리적으로 데이터 자체를 DB에서 없애는 것이고 Soft delete는 논리적인 범주에서만 없애는 것이다. 즉 Soft delete는 데이터 자체가 물리적으론 DB에 남아있다

 

논리적으로 삭제한다는 것 = Soft하게 삭제한다는 것이 어떤 의미일까? 삭제한 것처럼 여기겠다 라는 것이다. 테이블에 삭제여부를 나타내는 컬럼을 추가해 삭제여부가 표기돼있으면 삭제됐다고 보고, 삭제여부가 표기돼있지 않으면 삭제가 안 된 데이터로 본다는 것,

 

id 이름 삭제여부
1 권은비 false
2 카리나 true

 

그럼 이 Soft delete라는 것을 적용할 때 얻을 수 있는 장점은 뭘까?

 

당연히 뒷배가 있다는게 최고의 장점이다. 삭제처리된 데이터들을 따로 보고 싶을 때, 논리적으론 삭제됐지만 물리적으로는 데이터가 남아있는 셈이니 삭제된 데이터들을 따로 볼 때도 유용할 것이고, 실수로 인한 삭제가 발생했을 시 복원도 가능하다. 또한 물리적 삭제의 경우 delete라는 SQL을 쓰고 논리적 삭제는 update라는 SQL을 쓰는데, update가 delete에 비해 좀 더 빠른 속도를 낸다고도 한다.

 

하지만 Soft delete는 단점도 있다. 우선 물리적으로 데이터를 하드디스크에서 덜어내는 게 아니기 때문에 시간이 지날수록 물리적으로 쌓여있는 데이터들이 많아지며 DB 용량 자체가 커진다. 또 다른 단점은 where절 등으로 데이터들을 뽑아낼 때 삭제여부에 대한 처리도 반드시 함께 해줘야 하는 것. 이는 쿼리문을 직관적으로 이해하는 것에도 불편을 줄 가능성이 존재한다

 

따라서 이런 장단점들을 비교해보면서, 서비스 운영에 맞게 Soft delete를 사용할지 Hard delete를 사용할지 잘 고려해야 할 필요가 있을 것이다.

윈도우가 깔린 컴퓨터를 켜면, 처음에 어떤 유저로 들어갈 건지 고를 수 있다.

리눅스도 마찬가지다.

각각의 유저별로, 하나의 컴퓨터에 대해 할 수 있는 동작들이 다르다. 누구는 A를 할 수 있지만 누구는 할 수 없고 등등..

 

AWS도 이와 비슷하게 "유저들"이라는 개념이 있다.

조직원들이 쓰고 있는 전체 자원들에 대해, 누구는 A를 할 수 있지만 누구는 못 하게끔 설정이 가능하다.

 

AWS에서, "계정"과 "사용자"는 다른 개념이다.

계정은 root, 즉 리눅스에서의 super user같은 개념이다. 과금의 주체가 되는 주인이라고 보면 된다.

사용자는 이용자, 즉 root가 허가한 권한만 갖는 애들이라고 보면 된다. 위에서 방금 말했던 "유저들"이란 개념을 상기하자.

 

AWS계정을 처음 만들면 root 사용자로 시작하는 것이며, 모든 AWS서비스 및 리소스에 대한 전체 액세스 권한을 갖는다(모든 걸 다 할 수 있다는 의미).

그 후 root가 유저들을 만들면서 이 유저들한테 특정 권한들(어떤 리소스에 대해 어떤 동작을 허가한다와 같은)을 줄 수 있는 거다. 이것이 사용자라는 개념이라고 이해하면 된다. 관리자 유저한테는 ~~권한들을 주고, 개발자 유저한테는 ~~권한들을 주고, 감사자 유저한테는 ~~권한들을 주고 그런 식으로 세팅할 수 있는 것.

 

암튼.. 이런 것들에 대해 적용되는 개념이 IAM = Identity and Access Management이다.

 


IAM이라는 서비스와 구성요소

IAM은 한 줄로 말하자면 AWS에서 사용자를 만들고 권한을 주는 것을 통해 AWS 리소스에 대한 액세스를 안전하게 제어할 수 있게끔 하는 서비스이다. 각 유저들에 대한 권한 제어는 policy(=정책)라는 json포맷의 텍스트(쉽게 말하면 "어떤 걸 할 수 있다"들의 목록같은 거다)를 통해 이뤄지며, IAM은 다음과 같은 4개로 구성된다.

 

  • 사용자 : 위에서 말한 개념
  • 그룹 : 각 사용자들을 그룹핑 가능하며, 그룹에 정책을 지정하면 그룹원들이 동일한 권한들을 갖게 할 수 있다
  • 역할
  • 정책

 

먼저 정책에 대해 얘기해보자.

다시 한 번 말하자면 "어떤 걸 할 수 있다"라는 것들..즉 "권한들의 목록"이 정책이다 라고 보면 되며, 사용자들은 이렇게 연결된 정책에서 지정된 권한들만 쓸 수 있다.. 그렇기 때문에 사용자를 만들고 그룹을 배정해주든 정책을 연결해주든 아무것도 안 해준다면 사실상 그 놈은 아무고토 할 수가 없다. 왜냐하먄 어떠한 권한도 없는 셈이니까. 리소스 기반 정책과 자격증명 기반 정책으로 분류할 수 있다

 

  • 리소스 기반 정책 : "리소스"에 연결되는 정책. 예를 들면 S3 bucket에는 ~~만 액세스할 수 있다! 처럼
  • 자격 증명 기반 정책 : "사용자나 그룹 또는 역할"에게 권한을 부여하는 식으로 사용하는 정책. 예를 들면 넌 EC2에 대해서는 읽기만 가능해! 등등

 

여기서 자격 증명 기반 정책은 다음과 같이 두 종류로 분류할 수 있다

 

  • Inline Policy : 1 : 1로 사용되는 정책. 적용된 사용자가 없어지면 이 정책도 사라진다
  • Managed Policy : 1 : N으로 사용되는 정책. 즉 정책을 object화해서 다른 애들한테도 이 정책을 멕일 수 있다. 적용된 사용자가 사라져도 이 정책은 남아있다.

 

여기서 Managed Policy는 다음과 같이 두 종류로 분류할 수 있다. 이번이 마지막임ㅋㅋㅋㅋ

 

  • AWS managed Policy : AWS에서 미리 만들어둔 애들. 변경할 수 없다
  • Customer managed Policy : 사용자가 생성한 정책! 

 

참고로 Customer managed Policy를 만들 땐 Policy generator라는 걸 쓰는게 좋다. 하나하나 타이핑해서 json파일을 만드는 것보단 프로그램을 통해 편하게 만들 수 있기 때문. 

(또한 이렇게 정책들을 다 멕인 후에, simulator를 통해서 이런저런 정책들이 적용된 상황에서의 테스트가 가능하다)

 

 

그 다음으론, 역할에 대해 얘기해보자.

얘는 쉽게 말해서, 임시로 권한들을 부여하는 기능(즉 당일 회수되는 임시 출입증을 발급하는..)이라고 보면 된다. 내가 원래 다른 권한들을 가지고 있어도, 특정 역할을 잠깐 맡는다면 그 역할에 부여된 정책들을 임시로 사용할 수 있는 거다.

주의할 점은 병합의 개념이 아니기 때문에, 기존에 정책들을 가지고 있던 사용자가 역할을 받으면 원래 가지고 있던 정책들은 가려진다. 즉 역할을 통해 임시로 받는 권한들만 사용 가능해진다.

참고로, AWS lambda와 같은 AWS 서비스들도 역할이란 걸 받을 수 있으니 알아두면 좋다:)

 


IAM에서 정책을 평가하는 방법

그럼 IAM에서 정책은 어떤 순서대로 적용될까? 한마디로 사용자에게 부여된 권한들은 어떤 순서로 평가돼서 특정 리소스들에 대해 얘는 거부되는 거고, 얘는 승인하는건지 평가되는지 알아보자.

 

참고로, AWS는 어떤 권한에 대해 명시적으로 언급하지 않았다면 그 권한에 대해 암묵적으로 Deny, 즉 거부가 된다. 명시적으로 Allow, 즉 승인을 해줘야만 그 권한을 이용가능하다고 생각하면 된다.

 

암튼 평가 순서는

 

  1. 우선, 그 권한에서 이야기하는 작업이 명시적으로 거부되는지를 본다. 만약 명시적으로 거부했다면 더 볼 필요도 없이 해당 권한이 없는 걸로 결론짓고 제낀다. 명시적으로 거부되지 않았다면, 다음 스텝으로 넘어간다
  2. 그럼 그 작업이 명시적으로 승인되는지를 본다. 만약 명시적으로 승인됐다면 그 권한이 있는 걸로 결론지어진다. 만약 그렇지 않다? 이는 그 권한에 대해 아무런 명시도 안 한 것이니 암묵적으로 거부했다고 판단한다. 즉 해당 권한이 없는 것으로 결론짓는다

 

뭐 이런 식으로 해당 권한이 있고 없고가 결정된다고 보면 된다. 이렇게 평가된 정책들에 대해, 사용자에게 부여된 자격 증명 기반 정책들을 통과하고, 리소스들에 부여된 리소스 기반 정책(VPC 엔드포인트 정책, S3 버킷 정책 등등)을 통과해야 비로소 해당 리소스에 접근할 수 있게 되는 것이다.

 


IAM 권한 경계

자격 증명 기반 정책들이 IAM 사용자들에게 부여할 수 있는 최대 권한을 설정하는 기능이다. 즉 나한테 부여된 정책들 중 내가 쓸 수 있는 정책들을 제한해주는 기능이라고 보면 된다. 따라서 권한 경계와 나에게 부여된 자격 증명 기반 정책의 교집합이 내가 쓸 수 있는 정책이 될 것이다.

 


AWS orginizations

다중 계정을 관리하는 것을 말한다. 사용자들을 그룹핑해서 그룹을 만들어주던 것을 계정에도 그대로 적용해서, 계정들도 조직을 만들어 그룹핑함으로써 특정 조직은 어떤 걸 할 수 있게 하고, 특정 조직은 어떤 걸 못 하게 하고 이런 걸 설정할 수가 있다. 

이렇게 조직에 허가되는 정책을 SCP(Service Control Polity)라고 한다. 즉.. 사용자는 자신이 속한 계정이 속한 organization에서 허가된 권한들만 쓸 수 있는 거..

 

정리하면, 내가 만약 AWS에서 뭔가 작업하고 싶다면

 

  1. 일단 내가 속한 계정의 조직이 이 작업을 허용했는지 봐야 하고
  2. 적용된 IAM 권한 경계를 따져서 내가 할 수 있는 건지 봐야하고
  3. 내게 부여된 자격 증명 기반 정책이 이 작업을 허용하고 있는지

 

를 따지게 되는 거다.. 이렇게 보니 꽤나 복잡한 듯

후..삽질하다가 너무 어이없게 해결해서 쓴다. 사실 내가 무지했던 거지만..


플러터에서 dio를 통해 스프링부트에서 간단하게 만든 api를 테스트하는 중이었다. 근데 어머나?

SocketException: Connection refused (OS Error: Connection refused, errno = 111)

라는 오류가 떴다. 

 

포스트맨으로 할 때도 됐고,,웹에서 할 때도 됐는데,,왜 안 될까.. CORS문제인가하면서 이상한 뻘짓들을 했는데

답은 너무나도 간단했다.

 

안드로이드 에뮬레이터는 localhost를 사용할 수 없는 거였다

 

하..너무 당연하게 웹에서 구동하는 줄 알았따 ㅜㅠ 이거 땜에 뭔 고생을 한건지..

 

암튼, localhost대신 10.0.2.2를 쓰면 된다고 한다. 물론 프로덕션으로 가게 되면 다른 걸 설정해야됨!

 

https://developer.android.com/studio/run/emulator-networking?hl=ko 

 

Android Emulator 네트워킹 설정  |  Android 스튜디오  |  Android Developers

에뮬레이터는 앱에 복잡한 모델링 및 테스트 환경을 설정하는 데 사용할 수 있는 다목적 네트워킹 기능을 제공합니다.

developer.android.com

 

등장배경

어쩌다보니 꽤나 잘 그린 것 같은 그림이 나왔다

우리가 만든 서비스가 유저를 대신해서 구글에서 제공하는 서비스에 뭔가를 하고 싶은 일들이 생겼던 거다. 가령 구글 캘린더에 일정등록을 우리가 만든 서비스가 해준다든가, 등등.. 이를 위해서는 유저로부터 그가 사용하는 구글에 대해 접근할 수 있다는 허락을 받아야 한다.

 

가장 쉬운 방법은 당연히 유저로부터 구글ID와 PW를 받는 것. 우리가 만든 서비스가 유저가 준 ID, PW를 기억하면서 적재적소에 써먹으면 된다. 상당히 쉽고 강력한 방법이다.

하지만 당연히 이걸 실제로 써먹을 수는 없다. 유저입장에선 우리가 만든 서비스를 신뢰할 수 없을 것이며, 구글 입장에서도 유저가 아니라 제 3자인 우리 앱이 유저의 ID와 PW를 가지게 되니 여간 골치아픈게 아니다. 결국 보안적으로 가당치도 않은 상황이 된다.

 

이런 문제에 대한 해결책으로 등장한 것이 본 포스트에서 다룰 OAuth다.

 

 

OAuth?

Open Authorization의 줄임말. 사용자(user)와 구글과 같은 플랫폼 사이에서 제 3자에 해당하는 우리의 서비스가 해당 플랫폼에 있는 사용자의 데이터에 접근할 수 있는 권한을 위임받을 수 있는 표준 프로토콜이다. 이 프로토콜을 통해 사용자는 우리가 만드는 서비스에 ID, PW를 맡길(?) 필요가 없고, 구글 등에 있는 사용자의 데이터에 대해 접근할 수 있는 권한을 우리 서비스가 부여받을 수 있게 된다.

 

 

OAuth의 원리

한 문장으로 요약하자면

 

사용자의 요청을 통해 구글이 access token을 발급해주고, 그 토큰을 통해서 우리가 구글에 존재하는 사용자의 데이터에 접근이 가능해지는 것

 

이다. 이제 이 페이지를 닫으셔도 됩니다

좀만 더 원리를 디테일하게 설명하기 전, 용어 정리를 한 번 하고 가야 한다.

 

 

Resouce Owner

우리가 만든 서비스를 이용하면서 구글 등에 데이터를 가지고 있는 사람. 즉 사용자를 말한다

 

Resouce Server

구글과 같이 사용자의 리소스를 가지고 있는 서버, 즉 우리가 만든 서비스가 제어하고자 하는 리소스를 가지고 있는 애를 말한다. 인증 관련된 서버와 자원 관련된 서버로 구분하기 위해 Authorization Server와 Resource Server 2개로 분리하기도 하는데, 본 포스트에선 Resource Server 하나로 뭉탕치도록(?) 하겠다. 

 

Client

Resource Server의 리소스를 이용하고자 하는 서비스. 즉 우리가 만든 서비스를 말한다

 

 

그럼 이제 본격적으로 OAuth의 동작순서 및 원리에 대해 좀 더 알아보자.

 


동작순서는 다음과 같다

 

  1. Resource Owner(사용자)가 Client(우리가 만든 서비스)의 [구글 계정으로 로그인] 과 같은 버튼을 누른다
  2. Client는 이를 접수(?)하고 Resource Server(구글 등)에게 전달
  3. Resource Server는 Resource Owner에게 로그인 페이지를 보여주고, Resource Owner가 로그인한다
  4. Resouce Server는 인증이 성공되면 Resource Owner에게 Client가 특정 리소스에 접근해도 되냐는 질의를 한다
  5. Resouce Owner가 허락한다면, Resouce Owner가 Authorization code를 Resource Owner에게 전달하면서 Resource Owner를 사전에 약속(Client와 Resource Server가 사전에 약속한 것임)된 Redirect URI로 리다이렉트시킴 (Authorization code: 일종의 임시 암호)
  6. 이를 통해 Client도 Resouce Owner가 Resource Server로부터 전달받은 Authorization code를 알게 됨
  7. Client는 사전에 Resource Server와 합의해서 가지고 있던 client secret이란 걸 가지고 있음. 이걸 Authorization code와 함께 Resource Server에게 전달.
  8. Resoruce Server가 이에 대한 인증이 끝나면, Client에게 access token(허가증)을 발급!
  9. 이후 Client는 Resource Server에 존재하는 Resource Owner의 리소스에 접근할 때는 아까 받았던 access token을 활용

 

그럼 각 단계를 좀 더 뜯어보자. 그 전에, 위 순서에서 보면 사전에 약속된, 합의된 이런 말이 나온다. 그것도 포함해서 각 단계를 뜯어보자.

 


0. 일단 우리가 만든 서비스를 등록

우선 Client, 즉 우리가 만드는 서비스가 구글 즉 Resource Server를 이용하기 위해선 Resource Server에 우리가 널 쓸거라고 사전에 등록을 해야 한다. 이 방법은 구글, 카카오, 애플 등 플랫폼별로 조금씩 다르다.

 

플랫폼 별로 방법이야 당연히 다른데 공통적으로 수행하는 작업이 있다. 바로 Redirection URI를 등록하는 것! 이 URI는 구글과 같은 플랫폼이 인증이 성공한 사용자(구글로 로그인을 눌러서 자신의 구글 계정으로 로그인한..위 순서에서 3 ~ 5번 참조)를 리다이렉트 즉 이동시킬 URI다. 위 순서에서 알 수 있듯, 이는 Resource Server로부터 Authorization code를 받은 Resource Owner가 오게 되는 URI다. (CallBack URL로도 부르는 듯)

 

(음 내가 이해한 대로 설명하자면..유저한테 "유저야, 우리 서비스를 통해 구글에 접근하고 싶지? 그럼 너가 구글에 들러서 걔네한테 받은 임시 허가증을 우리 집 창문으로 들고 와!" 라고 하는 상황이다. 구글이 Resource Server고, 임시 허가증이 Authorization Code다. 그리고 우리 집 창문이 Redirect URI, 즉 사용자가 구글에서 임시 허가증을 받은 뒤 와야 하는 "지정된 장소"인 것. 근데 구글이 친절하게도 직접 택시를 태워서 사용자를 우리 집 창문 앞으로 보내주는 것, 즉 리다이렉트 시켜주는 거다)

 

암튼 이렇게 등록이 끝나면 Client Id와 Client Secret(위 순서에서 7번을 참조)를 발급받는다

 

  • Client Id : 등록된 우리 서비스를 Resource Server가 식별할 수 있는 식별자
  • Client Secret : Client Id에 대한 비밀번호. 외부에 노출되면 절대 안 된다

 

즉 이런 등록과정, 즉 사전협의를 통해 client와 resource server는 client id 및 client secret, 그리고 redirect uri를 아는 상태에서 시작한다.

 


1. Resource Owner가 Client의 [구글 계정으로 로그인] 과 같은 버튼을 누른다

걍 이거 말하는 거임

 

ChatGPT 로그인 화면

 

여기서 구글로 로그인 이런걸 유저가 누른다는 말!

 


2. Client가 이를 Resource Server로 전달

이때 전달하는 주소는 다음과 같은 형식이다 (물론 플랫폼 별로 조금씩 차이가 있을 수도..?)

 

https://resource.server/?client_id={client_id}&redirect_uri={redirect_uri}&scope={scope}

 

아까 사전 협의를 통해 Client가 Client Id와 Redirect URI를 알고 있음을 상기하자. scope는 client가 resource server로부터 인가받을 권한의 범위를 말한다고 생각하면 된다(구글의 모든 리소스에 접근할 수 있는 것보단 딱 필요한 것에만 접근할 수 있게끔 하는 것이 당연히 좋다)

 


3. Resource Server가 로그인 페이지를 보여주고 Resource Owner가 로그인한다

이거 말하는 거임

 


4. Resource Server는 인증이 성공하면 Resource Owner에게 Client가 특정 리소스들에 접근해도 되냐는 질의를 함

인증이 성공(즉 Resource Owner가 로그인에 성공하면)하면, Resource Server는 쿼리스트링 형식으로 넘어온 파라미터들을 보며 Client가 본인이 아는 그 놈이 맞는지 검사한다(즉 사전에 협의된 녀석이 맞는지 검사). Resource Server 역시 Client Id와 Redirect URI를 알고 있음을 다시 한 번 상기하자.

 

검사하는 내용은,

 

  1. 파라미터로 전달된 client id값과 동일한 값을 내가(Resource Server가) 가지고 있는가?
  2. 가지고 있다면 그에 대한 redirect uri가 파라미터로 전달된 redirect uri와 동일한가?

 

이렇게 검사한 후에, 특정 리소스들에 접근해도 되냐는 질의를 하는데 그건 이런 거 말하는 거임

 

 


5. Resource Owner가 허락한다면, Resource Server는 Authorization code를 Resource Owner에게 전달하며 Redirect URI로 리다이렉트시킨다

특정 리소스들에 대한 접근 질의에 Resource Owner가 허락한다면, Resource Server는 해당 Client Id에 대해 특정 user_id를 갖는 Resource Owner가 특정 scope에 대한 행동을 허가했다는 사실을 기록한다. (즉 특정 유저가 우리 서비스에 대해 A, B라는 행동을 해도 된다고 허락했다는 것을 기억하는 것)

 

 

그 후, Resource Owner에게 Authorization code라는 임시 암호를 발급해주면서 어떤 uesr에게 해당 Authorization code를 발급했는지 기록한다. 그와 동시에, 사전에 합의했던 Redirect URI로 Resource Owner를 리다이렉트시킨다.

 


6. 이를 통해 Client도 Resource Owner가 Resource Server로부터 발급받은 Authorization Code를 알게 됨

별도의 부연설명은 생략

 

 


7. Client는 사전에 합의한 후 받았던 Client Secret과 함께 Authorization code를 Resource Server에게 전달

 

 

이 때 다음과 같은 형식으로 보내게 된다

 

https://resource.server/token?
	grant_type=authorization_code&
	code={authorization_code}&
	redirection_uri={redirect_uri}&
	client_id={client_id}&
	client_secret={client_secret}

 

각각에 대해 설명하자면

 

  • grant_type : 항상 "authorization_code"라는 문자열로 설정
  • code : 전달받은 authorization code를 넣으면 됨
  • redrection_uri : 사전에 합의한 바로 그 redirection_uri 넣으면 됨
  • client_id : 사전에 합의하고 받은 바로 그 client id 넣으면 됨
  • client_secret : 사전에 합의하고 받은 바로 그 client secret 넣으면 됨

 


8. Resource Server가 이에 대한 인증이 끝나면, Client에게 access token을 발급함

Resource Server는 Client에게 전달받은 code(= authorization code)값과 자신이 아까 기록한(5번 참조) Authorization code를 대조하며 인증을 함. 이 과정이 성공적으로 끝나면, Resource Server는 아까 자신이 기록했던 Authorization code를 지우고 Client에게 Access Token을 발급하며 해당 토큰을 어떤 user_id에게 발급했는지를 기록한다.

 

 


9. 이후 Client는 발급받은 Access token을 이용해 활용

자세한 설명은 생략한다.

 

 


참고로, Refresh token을 발급해주기도 한다고 한다. Access token이 만료되면 Refresh token을 통해서 Access token을 재발급받는 것.

+ Recent posts