JWT 토큰이란 무엇인가.

아래 프로젝트를 진행하다가 궁금하여 알아보게 되었습니다. 

JWT 는 Json Web Token 의 약자로 사용자 인증과 권한 부여에 사용되는 토큰 기반의 인증 방식 입니다. 

https://memoryman.tistory.com/13

 

 

 

JWT 을 알아보기 전에 Token은 무엇이며 왜 사용하는가에 대해서 알고 넘어가겠습니다. 

왜 사용하는걸까요 Token...?

이유는 크게 2가지 존재합니다. 

첫번째는 사용자 인증을 위해서 사용 합니다. 

코스트코에서 물건을 구매하려면 회원권을 발행 받아야 하는데요. 

이때 코스트코 회원권과 토큰은 같은 개념이라고 봐주면 될 것 같습니다.

토큰과 회원권도 내가 사용자라는걸 인증해야 서비스를 이용할 수 있기 때문이죠.

유효기간이 있다는 점도 공통 부분이네요.

정리하면 토큰은 API 를 사용하기 위해 필요한 사용자 인증(회원권) 입니다. 

두번째는 서버 자원 절감 입니다. 

토큰을 사용하기전에는 서버에서 고객에 대한 정보를 세션 메모리에 저장해야 했습니다. 

해당 리소스를 클라이언트에게 돌리면서 메모리 자원을 아낄 수 있게 되었습니다. 

 

 

Token 사용 이유에 대해서 알아보았고 좀 더 들어가서 JWT 에 대해서 알아보겠습니다. 

JWT 은 뭐가 다를까요. 

첫번째 일반(Session, Opaque) Token  의 경우 단순 문자열로 구성되어 있지만 JWT 의 경우 Header, PayLoad, Signature 3가지 구조로 나눠져 있습니다. 

두번째 JWT 안에 정보를 포함할 수 있습니다. 일반적으로 Token 값을 기준으로 DB 에서 데이터를 가져오는 반면 JWT 은 사용자 정보, 유효 시간 데이터가 인코딩 되어 존재 합니다. 

아무나 디코딩할 수 있는거 아니냐할 수 있지만 구조 중 Signature 때문에 위변조는 불가 합니다. 

추가적으로 Token 자체에 데이터를 담고 있는 것을 Stateless 이라하고 일반 Token 처럼 서버에서 관리하는 Token 을 Stateful 라고 합니다. 

 

 

JWT 구조는 Header, PayLoad, Signature 3가지로 나뉜다고 말씀드렸는데요. 

Header 의 역할은 암호화 알고리즘과 토큰 타입을 지정 합니다. 

"aig" 키는 알고리즘을 "type" 키는 타입을 의미 합니다. 

{
  "alg": "HS256",  // 해시 알고리즘 (예: HMAC-SHA256)
  "typ": "JWT"     // 타입 (고정: JWT)
}

PayLoad 에는 데이터가 포함 되어 있습니다. 

하나의 데이터를 클레임(claim) 이라고 부르며 아래 데이터 기준으로 "userId", "role", "exp" 3개의 클레임이 존재 하는 경우가 됩니다.

예시에는 디코딩 데이터로 보이지만 실제로 전달되는 데이터는 인코딩되어 전달 됩니다. 

{
  "userId": "123456",
  "role": "admin",
  "exp": 1687871800   // 만료 시간 (UNIX timestamp)
}

마지막 구조 Signature(서명) 입니다. 

위조를 막기 위해서 사용되고 생성 방식은 Header+Payload+SecretKey 3가지를 조합 합니다. 

알고리즘으로는 비대칭키(RS256) 방식과 대칭키(HS256) 방식이 존재 합니다. 

Signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),"memorymay_secretKey")

 

 

자 그럼 실제 .NET 환경에서 JWT 적용해 보도록 하겠습니다. 아래는 미들웨어에 적용한 JWT 코드 입니다. 

위에 변수들은 appsettings.json 파일에서 데이터를 읽어오는 역할을 합니다. 

builder.Services.AddAuthentication("Bearer") 코드는 우리가 사용할 인증 방식을 Bearer 설정 하겠다 의미 입니다. 

Bearer 는 영어로 소지자 의미를 담고 있으며 한마디로 이 토큰을 가진 사람은 인증된 사용자라는 뜻 입니다.
헤더에 Authorization: Bearer {token} 형식으로 사용 됩니다. 

AddJwtBearer 는 Bearer 사용에 대한 설정값으로 주석 부분 참고 바랍니다. 

var config = builder.Configuration;
var jwtSettings = config.GetSection("JwtSettings");

#region JWT 

builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true, // 토큰 발급자 검증 여부
            ValidateAudience = true, // 토큰 대상 검증 여부
            ValidateLifetime = true, // 토큰 만료시간 검증 여부
            ValidateIssuerSigningKey = true, // 서명키 검증 여부
            ValidIssuer = jwtSettings["Issuer"], // 허용할 발급자
            ValidAudience = jwtSettings["Audience"], // 허용할 대상자
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]!)) // 보안키 생성
        };
    });

#endregion

 

 

아래는 json 파일 입니다. 위 코드 변수들이 불러오는 대상 데이터 입니다. 

우리는 Hash256 알고리즘을 사용할 예정이므로 secretKey 길이가 32bit , 즉 영어 기준 32자 이상이어야 합니다. 

256byte 미만시 토큰 생성시에 문제가 발생 합니다. 

  "JwtSettings": {
    "SecretKey": "MemoryMan_SecretKey1234567890_LONGER!",
    "Issuer": "MemoryManIssuer",
    "Audience": "MemoryManAudience",
    "ExpiresInMinutes": 60
  }

 

 

JWT 에는 크게 인증(UseAuthentication)과 권한(UseAuthorization) 2가지가 존재 합니다.

기본적으로 우리가 사용하는 기능은 "인증" 입니다. 

JWT 를 정상적으로 발급 받았는지만 체크하는 단계가 인증 입니다. 

"권한" 은 인증이 된 사용자 중 등급을 나눕니다. 

예를 들면 모든 API 를 사용할 수 있는 Admin 등급과 일반 조회 정도만 사용할 수 있는 User 등급으로 나눌 수 있겠네요.

저는 인증과 권한 2가지를 모두 사용할 예정으로 미들웨어에 모두 추가하도록 하겠습니다. 

var app = builder.Build();

#region 인증 미들웨어

app.UseAuthentication(); // 인증 정보 확인
app.UseAuthorization(); // 권한 확인

#endregion

 

 

미들웨어 단계에서의 준비는 모두 완료 했습니다. 

우리는 컨트롤러에서 JWT 를 생성하고 사용해보도록 하겠습니다. 

인증과 권한을 부여하는 것은 생각보다 간단한데요. 

액션 함수에 [Authorize] 어트리뷰트만 선언해주면 "인증" 을 실행 합니다. 

[Authorize] 의 Roles 프로퍼티를 선언하면 문자열(Admin) 값을 가져야만 접근가능한 "권한" 이 적용 됩니다. 

즉 GetUserAsync API 는 토큰을 발급 받기만하면 접근 가능하고 GetAsync API 는 토큰을 발급받고 Admin 권한이여야 접근 가능 합니다. 

        [Authorize]
        [HttpGet]
        public async Task<IActionResult> GetUserAsync([FromQuery] HomeDto homeDto)
        {
            return Ok();
        }

        [HttpGet]
        [Authorize(Roles = "Admin")]
        public async Task<IActionResult> GetAsync([FromQuery] HomeDto homeDto)
        {
            return Ok();
        }

 

 

이제 토큰만 생성해서 전달해주면 마무리 될 것 같네요.

최초 사용자에게 데이터를 받고 SelectMember 메서드를 통해 DB 에 있는 사용자 데이터를 가져 옵니다. 

사용자 데이터를 JWT 생성 메서드 GenerateToken 파라미터로 전달 합니다. 

        [HttpGet]
        [Route("JWT")]
        public async Task<IActionResult> GetToken([FromQuery] HomeDto homeDto)
        {
            try
            {
                #region 사용자 데이터 추출

                MemberDto memberDto = new MemberDto();
                memberDto.Name = homeDto.Name;

                var data = await _home.SelectMember(memberDto);

                #endregion

                #region JWT 발급

                memberDto.NameIdentifier = data.MemberSeq.ToString();
                memberDto.Name = data.MemberName;
                memberDto.Role = data.RoldId == MemberEnum.Admin ? "Admin" : "User";

                var token = await _jwt.GenerateToken(memberDto);

                #endregion

                return Ok(token);
            }
            catch (Exception ex)
            {
                return BadRequest();
            }
        }

 

 

여기서 주의 깊게 봐주실 부분은 claims 의 Role 입니다. 

해당 부분이 [Authorize(Roles = "Admin")] 선언된 액션 함수에 접근할 수 있는지 없는지 결정 됩니다. 

DB 에 저장되어 있는 Role 값이 관리자라면 Admin 값이 들어갈테고 아니라면 User 값이 들어가게 됩니다. 

        public async Task<string> GenerateToken(MemberDto memberDto)
        {
            var jwtSettings = _configuration.GetSection("JwtSettings");
            var secretKey = jwtSettings["SecretKey"];
            var issuer = jwtSettings["Issuer"];
            var audience = jwtSettings["Audience"];
            var expires = DateTime.UtcNow.AddMinutes(Convert.ToDouble(jwtSettings["ExpiresInMinutes"]));

            var claims = new[]{
                new Claim(ClaimTypes.NameIdentifier, memberDto.NameIdentifier),
                new Claim(ClaimTypes.Name, memberDto.Name),
                new Claim(ClaimTypes.Role, memberDto.Role)
            };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: issuer,
                audience: audience,
                claims: claims,
                expires: expires,
                signingCredentials: creds
            );

            return new JwtSecurityTokenHandler().WriteToken(token);
        }

 

 

프로젝트에 JWT 잘 녹여서 사용해도록 하겠습니다. 

그럼 저는 다시 프로젝트로 돌아갑니다.

오늘도 알아가면서 메모리!

https://memoryman.tistory.com/13

 

 

'IT' 카테고리의 다른 글

완벽하지 않아도 괜찮다.  (1) 2025.07.13
일을 하던 대로만 계속 잘하는 건 사실 일을 잘하고 있는게 아니다!  (5) 2025.07.03
AWS MFA 분실했을 경우, 해결 방법  (1) 2025.05.16
공장에서 사용하는 MES란 무엇인가.  (2) 2025.05.09
객체 지향 방법론(OOP)란 무엇인가.  (0) 2025.04.23
'IT' 카테고리의 다른 글
  • 일을 하던 대로만 계속 잘하는 건 사실 일을 잘하고 있는게 아니다!
  • AWS MFA 분실했을 경우, 해결 방법
  • 공장에서 사용하는 MES란 무엇인가.
  • 객체 지향 방법론(OOP)란 무엇인가.
memoryman
memoryman
memoryman 님의 블로그 입니다.
  • memoryman
    MEMORYMAN STACK
    memoryman
  • 전체
    오늘
    어제
    • 분류 전체보기 (55)
      • Dart (11)
      • Python (2)
      • C# (1)
      • DB (5)
      • Algorithm (1)
      • Project (4)
      • IT (12)
      • .NET (7)
      • Flutter (12)
  • 블로그 메뉴

    • 프로필
    • 방명록
    • 메모장
    • 자소서
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    D
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
memoryman
JWT 토큰이란 무엇인가.
상단으로

티스토리툴바