인증|인가

[인증/인가] JWT토큰 생성 과정과 원리에 대해서

코카멍멍 2023. 7. 5. 14:26
반응형

목차

  1. 클라이언트 로그인
  2. 서버 인증
  3. JWT 토큰 생성
  4. 토큰 암호화
  5. 토큰 검증
  6. 토큰 전달
  7. 클라이언트 요청

JWT토큰이란??

JWT(JSON WEB TOKEN)은 당사자 간에 정보를 JSON 개체로 안전하게 전송하기 위한 간결하고 독립적인 방법을 정의하는 개방형 표준(RFC 7519)입니다. 이 정보는 디지털 서명되어 있으므로 확인하고 신뢰할 수 있습니다. JWT는 HS(HMAC 알고리즘 포함) 또는 RSA 또는 ECDSA를 사용하는 공개/개인 키 쌍을 사용하여 서명할 수 있습니다.

사실 이렇게 개념을 들어도 이게 어떻게 만들어지고 어떻게 사용되는지 잘 체감이 안됐습니다. 그래서 그 과정에 대해서 어떻게 만들어지고 어떤방식으로 진행되는지 알아봤습니다.

토큰에 대해서 간략하게 말하자면 서버에게 내가 나임을 증명할 수 있는 것이라고 생각하면 좋을것 같습니다.

JWT토큰 HS기반 설명

HS256이란 HMAC(Hash-based Message Authentication Code) SHA 256 (Secure Hash Algorithm)의 약자로 헤더, 페이로드,비밀키를 SHA256으로 암호화 한 방식입니다.

밑에 JWT토큰이 생성되는 과정은 HS256방식을 기반으로 설명드리겠습니다. RS256방식도 과정은 매우 유사하나 비대칭키를 사용한다는 점에서 차이가 있습니다.

토큰 생성시점?

토큰을 생성하고 발급하는건 서버 마음입니다. 위에서 말씀 드렸듯이 JWT 토큰은 서버에게 내가 나임을 증명할 수 있는것이라고 했습니다. 그렇다면 로그인을 할 때 내가 나임을 증명하니까 일반적으로 로그인할 때 발급하게 됩니다.

1. 클라이언트 로그인

로그인화면은 많이 익숙하실것 같습니다. 아이디 비밀번호를 입력하고 서버에 결과를 기다립니다 이제 다음 서버는 어떻게 검증하는지 확인해보겠습니다

2. 서버 인증

    @PostMapping("/login")
    public Response<MemberLoginResponse> login(@RequestBody MemberJoinRequest request){
        String token = memberService.login(request.getUsername(), request.getPassword());

        return Response.success(new MemberLoginResponse(token));
    }

서버는 클라이언트가 전달한 아이디, 비밀번호를 받아서 데이터베이스에서 사용자의 존재 여부를 조회합니다. 그래서 존재한다면 토큰을 발급하고 존재하지 않으면 존재하지않는다라고 예외를 사용자에게 전달합니다.

사용자가 서버에 존재했을 때

{
    "resultCode": "SUCCESS",
    "result": {
        "token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjg4NTI4Mzk4LCJleHAiOjE2OTExMjAzOTh9.nKespnex3zaEd_TOK5kTfJ3J9vkKNJaR98NRUiyHaQk"
    }
}

성공시 반환 형식인데 이것도 서버마다 형태가 조금씩 다를 수 있습니다

사용자가 서버에 존재하지 않을 때

{
    "resultCode": "NOT_EXISTS_USERNAME",
    "result": null
}

존재하지 않을 때 반환 값입니다. 존재하지않을 때 에러 코드와 같이 결과값을 반환해줬습니다.

3. JWT 토큰 생성

Base64UrlEncode(헤더) + "." + Base64UrlEncode(페이로드) + "." + HS256(Base64UrlEncode(헤더) + "." + Base64UrlEncode(페이로드), 비밀 키)

헤더와, 페이로드는 base64 방식으로 인코딩합니다. base64 손쉽게 디코딩을 할 수 있습니다. 그러니 여기에 민감한 정보를 넣으면 보안에 취약해질 수 있습니다.

4. JWT 토큰 암호화

Base64UrlEncode(헤더) + "." + Base64UrlEncode(페이로드) + "." + HS256(Base64UrlEncode(헤더) + "." + Base64UrlEncode(페이로드), 비밀 키)

앞서 말했듯이 헤더와 페이로드 부분은 인코딩 한거라 디코딩을 통해 확인할 수 있습니다. Base64UrlEncode(헤더) + "." + Base64UrlEncode(페이로드) + "." + HS256(Base64UrlEncode(헤더) + "." + Base64UrlEncode(페이로드), 비밀 키) 비밀키를 이용하여 SHA256 암호화 방식으로 단방향 암호화를 사용합니다. 여기서 사용되는 비밀 키는 외부에 노출이 되지 않게 잘 관리해야 합니다.

5. JWT 토큰 검증

이제 토큰이 만들어지는 과정은 알 것 같습니다. 하지만 서명확인 키는 복호화도 안되는데 어떻게 검증한다는 거죠? 라는 의문점이 나올 수 있습니다.
SHA256 방식은 단방향이지만 동일한 input값을 암호화 한다면 결과값은 동일한 output이 나온다는 겁니다.

Base64UrlEncode(헤더) + "." + Base64UrlEncode(페이로드) + "." + HS256(Base64UrlEncode(헤더) + "." + Base64UrlEncode(페이로드), 비밀 키)

앞에서 말했듯이 base64방식은 디코딩을 통해 읽을 수 있습니다. 그래서 헤더 정보와 페이로드 정보를 디코딩 하고 서버에서 꽁꽁숨겨놓은 비밀키를 이용해 JWT토큰을 다시 만드는 겁니다. 그러고 비교하면 JWT 토큰이 정상적인지 확인이 가능합니다

6. JWT 서버에서 토큰 전송

사용자가 서버에 존재했을 때

{
    "resultCode": "SUCCESS",
    "result": {
        "token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjg4NTI4Mzk4LCJleHAiOjE2OTExMjAzOTh9.nKespnex3zaEd_TOK5kTfJ3J9vkKNJaR98NRUiyHaQk"
    }
}

이건 개발진들 마음이긴 합니다. 어떻게 전송하고 어떻게 관리할건지 개발진들의 컨벤션에 맞춰서 사용자에게 토큰을 전송합니다.
그리고 브라우저에서는 받아서 Local-Storage나 Cookie에다가 저장합니다.

localStorage.setItem('token', res.data.result.token);

7. 클라이언트 요청

클라이언트에서는 서버에 요청하기 위해서는 header에 토큰정보를 담아줍니다.

    axios({
      url: '/api/v1/posts?size=5&sort=id&page=' + pageNum,
      method: 'GET',
      headers: {
        Authorization: 'Bearer ' + localStorage.getItem('token'),
      },
    })

Bearer토큰이란?

Bearer 토큰은 토큰을 소유한 사람에게 액세스 권한을 부여하는 일반적인 토큰 클래스입니다. 액세스 토큰, ID 토큰, 자체 서명 JWT는 모두 Bearer 토큰입니다.

토큰의 필드명은 Authorization value는 'Bearer'로 시작하는 일종의 약속입니다. 자세한 내용을 보시려면 아래 링크에서 확인하세요

구글 클라우드
JWT IO

 
 

 

 

반응형