Rest API Template 만들기 - EP 05(AccessToken, RefreshToken with JWT)

2023. 1. 2. 00:00ASPNET/ASPNET 7

반응형

기존에 TokenGenerator 를 통해서 Create 시에 Guid 를 이용해 token 을 생성했다. 

이 token 을 이용하는 것도 단순히 사용하기에는 좋은 방법이다. 

하지만 우리는 user 정보등을 token 에 포함하여 사용하고 싶을 수 도 있다. 

JWT (Json Web Token) 가 이러한 문제를 해결해 준다. 

 

그래서 이제 여기서는 TokenGenerator 를 통해 JWT 를 발행 하는 방법을 알아볼것이다. 

Shared/Interfaces/ITokenGenerator.cs

using ApiServer.Shared.Injectables;

namespace ApiServer.Shared.Interfaces;

public interface ITokenGenerator : ISingleton
{
    string Create(string id, string role = "user");
    string CreateRefreshToken();
}

 

Token config 을 설정하기 위해  TokenManagement 를 appsettings 에 설정하고

관련 class 를 Shared/Models 밑에 만든다.

 

appsettings.json 

  ...
  "TokenManagement": {
    "Secret": "Any String used to sign and verify JWT Tokens,  Replace this string with your own Secret",
    "Issuer": "ApiServer",
    "Audience": "ApiServer",
    "AccessExpiration": 30,
    "RefreshExpiration": 1440
  }
  ...

Shared/Models

namespace ApiServer.Shared.Models;

public class TokenManagement
{
    public string Secret { get; set; } = string.Empty;
    public string Issuer { get; set; } = string.Empty;
    public string Audience { get; set; } = string.Empty;
    public int AccessExpiration { get; set; }
    public int RefreshExpiration { get; set; }
}

 

JwtBearer token 관련 library 를 사용하기 위해서 다음 package 를 설치 한다. 

 

개발자 명령 프롬프트로 이동

Ctrl + ` 또는 View --> Terminal click --> 개발자 명령 프롬프트로 이동

개발자 명령 프롬프트에서 RestApiServerWebApp 프로젝트 폴더로 이동

**이미 프로젝트 폴더에 있다면 이동하지 않아도 된다. **

(현재 [솔루션명].sln 파일이 있는 폴더라면 [프로젝트명].csproj 파일이 있는 폴더로 이동)

다음 실행

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

 

Shared/Utility/TokenGenerator.cs

using ApiServer.Shared.Interfaces;
using Microsoft.IdentityModel.Tokens;
using System.Dynamic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace ApiServer.Shared.Utility;

public class TokenGenerator : ITokenGenerator
{
    private readonly IConfiguration _configuration;

    public TokenGenerator(IConfiguration configuration)
	{
        _configuration = configuration;
    }

    public string Create(string id, string role = "user")
    {
        dynamic token = new ExpandoObject();
        var tokenSection = _configuration.GetSection("TokenManagement");

        token.Secret = tokenSection.GetValue<string>("Secret");
        token.Issuer = tokenSection.GetValue<string>("Issuer");
        token.Audience = tokenSection.GetValue<string>("Audience");
        token.AccessExpiration = tokenSection.GetValue<int>("AccessExpiration");
        token.RefreshExpiration = tokenSection.GetValue<int>("RefreshExpiration");

        if (token is null) throw new Exception("TokenManagement configuration need. check your configuration");
        var now = DateTime.UtcNow;

        var claims = new[]
            {
                    new Claim("Name", id),
                    new Claim("Role", role),
                    new Claim("Ticks", now.AddMinutes(token.AccessExpiration).Ticks.ToString())
                };

        var jwtToken = new JwtSecurityToken(
        token.Issuer,
        token.Audience,
        claims,
        expires: now.AddMinutes(token.AccessExpiration),
        signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)), SecurityAlgorithms.HmacSha512Signature));
        return new JwtSecurityTokenHandler().WriteToken(jwtToken);
    }
    
    public string CreateRefreshToken() => Guid.NewGuid().ToString("N");
}

 

이제 login.cs 도 수정하자

 

Features/User/Login.cs

...
public class Response
{
    public bool Result { get; set; }
    public Error? Error { get; set; }
    public string? AccessToken { get; set; }
    public string? RefreshToken { get; set; } // 추가
}
...
public class CommandHandler : IRequestHandler<Command, Response>
{
    private readonly ITokenGenerator _tokenGenerator;

    // ITokenGenerator 를 Constructor injection 
    public CommandHandler(ITokenGenerator tokenGenerator)
    {
        _tokenGenerator = tokenGenerator;
    }
    public Task<Response> Handle(Command request, CancellationToken cancellationToken)
    {
        var response = new Response { Result = false };
        string? accessToken = null;
        string? refreshToken = null;

        if (request.Pasword == "1234")
        {
            accessToken = _tokenGenerator.Create(request.Id); // jwt Token 생성)
            refreshToken = _tokenGenerator.CreateRefreshToken();
            response.Result = true;
        }
        else
        {
            response.Error = new Error()
            {
                Code = "02",
                Message = $"Login Failed, check your password"
            };
        }
        response.AccessToken = accessToken;
        response.RefreshToken = refreshToken;
        return Task.FromResult(response);
    }
}

 

swagger 를 통해 accesstoken 과 refreshtoken 이 제대로 생성되는지 확인해 보자.

위와 같이 accessToken 과 refreshToken 이 생성되는걸 볼 수 있다. 

accessToken 은 내부에 claim 을 통해 user 관련 정보가 들어 있는 JWT Token 이다.

refreshToken 은 guid 형태로 만들어진 token 이다. 

 

관련영상

https://youtu.be/XJYzZuONRjk

 

반응형