Flutter/ jwt token을 decoding하고, 정보를 얻어보자

koeyhoyh·2022년 10월 6일
3

App_Flutter

목록 보기
19/19
post-thumbnail

배경

token을 사용할 때, Refresh token과 access token으로 나누어 보통 사용합니다.
access token의 만료 시간이 임박했을 때만 refresh해주고 싶은 마음이 있어서, 찾아봤습니다. 같은 상황이신 분들께도 도움이 되었으면 좋겠습니다 : )


목차

0. Reference

1. jwt란?

2. Flutter 안에서 jwt decoding 방법

3. 요약


Reference

✅jwt token 명세 : https://datatracker.ietf.org/doc/html/rfc7519
✅json이 궁금하다면? : https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/JSON
✅decoding 방법: https://prafullkumar77.medium.com/flutter-how-to-decode-jwt-token-using-dart-a9657aeaeedf
(이분이 작성하신 글에 제 설명을 조금 더 덧붙였습니다!)


1. jwt가 뭐죠?

json web token의 약자입니다. 간략하게 특징을 살펴보자면

  • 전자서명 - 안의 내용물(json)이 위/변조 되었는지 확인 가능.
  • 권한이나 서비스 이용 정보가 token 안에 존재함.
  • 클라이언트에서 저장. (token의 특징)
  • base64 URL-safe encoding 방식 사용.
    (URL-safe - url로 이용할 수 있는 문자만 사용.)

token 인증 방식은 대개 이런 식으로 이루어집니다.

  1. 클라이언트가 로그인 등의 인증 방식으로 서버에 접근 -> 서버에서 jwt 발급
  2. 클라이언트에서 다른 권한이 필요한 api를 사용할 때 header에 jwt를 넣어 사용.

jwt는 해당 구조를 가지고 있습니다.

Header - 해당 토큰이 '어떤 알고리즘을 이용해' 서명되었는지(암호화되었는지) 식별합니다.
Payload - 전달하고자 하는 내용을 담고 있습니다. 표준 필드도 있지만, 사용자가 정의해 사용할 수 있습니다.
Signature - base64 URL-safe encoding 방식 + 해싱을 적용한 header 와 payload를 비밀키로 서명한(암호화한) 것입니다.

각 header, payload, signature은 '.'으로 구분되어 나타납니다.

jwt의 예시입니다.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9. // <- header와 payload의 구분.
eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjY1MDQ5NzIwLC
JpYXQiOjE2NjUwNDI1MjAsImp0aSI6ImI3NWI5OGJhMWMzZDQ5ZWZh
OGQ4ZThhODUyZGFkYjY2IiwidXNlcl9pZCI6MX0. // payload와 signature의 구분.
PlRA_eHDgtfEYAIP0vDRdCwjx2mqn4imCSFGTW-2FfU

2. 그래서 Flutter 에서는 어떻게 decoding하나요?

우리는 header, signature의 정보가 필요한 것이 아니라, 보통 payload의 데이터를 보고자 합니다.

먼저, token이 정상적인지 간단하게 검사합니다.

final data = token.split('.');
if (data.length != 3) {
	Throw Exception('invalid token');
  }

그 후, base64 URL-safe encoding 된 문자열을 decode 합니다.

import 'dart:convert';
String decodeBase64(String str) {
  String output = str.replaceAll('-', '+').replaceAll('_', '/');
 // 하단 설명 참고.
  switch (output.length % 4) {
    case 0:
      break;
    case 2:
      output += '==';
      break;
    case 3:
      output += '=';
      break;
    default:
      throw Exception('Illegal base64url string!"');
  }
  return utf8.decode(base64Url.decode(output));
}

'-' 와 '_'를 왜 '+' 와 '/'로 바꾸어주는 걸까요??

base64 encoding 방법은 2진수의 데이터를 문자 코드에 영향을 받지 않게 공통 ascii 영역의 문자들로 바꾸는 인코딩 방식입니다. 64개의 ascii code를 사용하는데 a-z, A-Z, 0-9, '+', '/' 를 사용하고 있습니다.

다만, '+', '/' 특수 문자는 전송하면 정상적으로 해당 값이 전송되지 않습니다. 그래서 '+'와 '/'를 '-'와 '_'로 바꾸어 준 것이 URL-safe 입니다.

'==' 와 '='는 뭔데 결과에 덧붙여주는 건가요??

==와 =는 패딩입니다. 0과 같은 역할을 하고 있습니다.

base64는 이런 식으로 encoding 합니다. 그러면 character가 하나라면, 혹은 두 개라면?? 나머지 공간은 무언가로 채워주어야 하는데 이 때 사용되는 것이 '=', '=='입니다.

그래서 '='와 '=='를 결과에 덧붙여 주는 것입니다.

이렇게 만들어낸 String은 'dart:convert' 라이브러리를 이용해 decode하면 됩니다.

전체 코드

import 'dart:convert';

String _decodeBase64(String str) {
  String output = str.replaceAll('-', '+').replaceAll('_', '/');

  switch (output.length % 4) {
    case 0:
      break;
    case 2:
      output += '==';
      break;
    case 3:
      output += '=';
      break;
    default:
      throw Exception('Illegal base64url string!"');
  }

  return utf8.decode(base64Url.decode(output));
}

Map<String, dynamic> parseJwtPayLoad(String token) {
  final parts = token.split('.');
  if (parts.length != 3) {
    throw Exception('invalid token');
  }

  final payload = _decodeBase64(parts[1]);
  final payloadMap = json.decode(payload);
  if (payloadMap is! Map<String, dynamic>) {
    throw Exception('invalid payload');
  }

  return payloadMap;
}

## 요약
  1. jwt는 base64 URL-safe encode 되었다.
  2. 따라서 '-'와 '_'를 '+'와 '/' 로 바꾸어주고,
    '=', '==' padding을 붙여 주어야 한다.
  3. 'dart:convert' 라이브러리를 이용해 utf8.decode('decoding String');
    바꾸어주면 끝! 서버가 클라이언트에게 보낸 데이터를 볼 수 있다.


    혹시 틀린 점이나, 덧붙이면 좋은 점들이 있다면 댓글이나 메일로 알려주시면 정말 감사하겠습니다.🥰 다들 좋은 하루 되세요!!
profile
내가 만들어낸 것들로 세계에 많은 가치를 창출해내고 싶어요.

2개의 댓글

comment-user-thumbnail
2022년 10월 23일

유익한 정보 감사합니다 퍼가요~

답글 달기
comment-user-thumbnail
2023년 2월 17일

감사합니다 : )

답글 달기