dotnet user-jwts를 사용하여 개발 중인 JSON Web Token 관리

작성자: Rick Anderson

dotnet user-jwts 명령줄 도구는 앱별 로컬 JSON 웹 토큰(JWT)을 만들고 관리할 수 있습니다.

개요

dotnet user-jwts [<PROJECT>] [command]
dotnet user-jwts [command] -h|--help

설명

프로젝트별 로컬 JSON 웹 토큰을 만들고 관리합니다.

인수

PROJECT | SOLUTION

명령을 적용할 MSBuild 프로젝트입니다. 프로젝트 파일을 지정하지 않으면 MSBuild는 현재 작업 디렉터리에서 proj로 끝나는 파일 확장명이 있는 파일을 검색하고 그 파일을 사용합니다.

명령

명령 설명
clear 프로젝트에 대해 발급된 모든 JWT를 삭제합니다.
create 새 JSON 웹 토큰을 발급합니다.
remove 지정된 JWT를 삭제합니다.
key JWT를 발급하는 데 사용되는 서명 키를 표시하거나 다시 설정합니다.
list 프로젝트에 대해 발급된 JWT를 나열합니다.
print 지정된 JWT의 세부 정보를 표시합니다.

만들기

사용법: dotnet user-jwts create [options]

옵션 설명
-p | --project 작업할 프로젝트의 경로입니다. 기본값은 현재 디렉터리의 프로젝트입니다.
--scheme 생성된 토큰에 사용할 스키마 이름입니다. 기본값은 'Bearer'입니다.
-n | --name JWT를 만들 사용자의 이름입니다. 기본값은 현재 환경 사용자입니다.
--audience JWT를 만들 대상 그룹입니다. 기본값은 프로젝트의 launchSettings.json에 구성된 URL입니다.
--issuer JWT의 발급자입니다. 기본값은 'dotnet-user-jwts'입니다.
--scope JWT에 추가할 범위 클레임입니다. 각 범위에 대해 한 번 지정합니다.
--역할 JWT에 추가할 역할 클레임입니다. 각 역할에 대해 한 번 지정합니다.
--claim JWT에 추가할 클레임입니다. 각 클레임에 대해 "name=value" 서식으로 한 번 지정합니다.
--not-before JWT가 'yy-MM-dd [[HH:mm[:ss]]]]] 형식으로 유효하지 않아야 하는 UTC 날짜 및 시간입니다. 기본적으로 JWT가 만들어진 날짜 및 시간으로 설정됩니다.
--expires-on JWT가 'yyyy-MM-dd [[[ [HH:mm]]:ss]] 형식으로 만료되어야 하는 UTC 날짜 및 시간입니다. 기본값은 --not-before 날짜 이후 6개월입니다. 이 옵션은 --valid-for 옵션과 함께 사용하면 안 됩니다.
--valid-for JWT가 만료되어야 하는 기간입니다. 일은 'd', 시간은 'h', 분은 'm', 초는 's'와 같은 기간 형식을 사용하여 지정합니다(예: 365d'). 이 옵션은 --expires-on 옵션과 함께 사용하면 안 됩니다.
-o | --output 명령의 출력을 표시하는 데 사용할 형식입니다. '기본값', '토큰' 또는 'json' 중 하나를 사용할 수 있습니다.
-h | --help 도움말 정보 표시

예제

다음 명령을 실행하여 빈 웹 프로젝트를 만들고 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 패키지를 추가합니다.

dotnet new web -o MyJWT
cd MyJWT
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Program.cs의 내용을 다음 코드로 바꿉니다.

using System.Security.Claims;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();
builder.Services.AddAuthentication("Bearer").AddJwtBearer();

var app = builder.Build();

app.UseAuthorization();

app.MapGet("/", () => "Hello, World!");
app.MapGet("/secret", (ClaimsPrincipal user) => $"Hello {user.Identity?.Name}. My secret")
    .RequireAuthorization();

app.Run();

이전 코드에서 /secret에 대한 GET 요청은 401 Unauthorized 오류를 반환합니다. 프로덕션 앱은 자격 증명 집합을 통해 로그인에 대한 응답으로 STS(보안 토큰 서비스)에서 JWT를 가져올 수 있습니다. 로컬 개발 중에 API를 사용하기 위해 dotnet user-jwts 명령줄 도구를 사용하여 앱별 로컬 JWT를 만들고 관리할 수 있습니다.

user-jwts 도구는 사용자 비밀 도구와 개념상 유사하며 로컬 컴퓨터의 개발자에게만 유효한 앱의 값을 관리하는 데 사용할 수 있습니다. 실제로 user-jwts 도구는 user-secrets 인프라를 활용하여 JWT가 서명된 키를 관리하고 키가 사용자 프로필에 안전하게 저장되도록 합니다.

user-jwts 도구는 값이 저장되는 위치 및 방법과 같은 구현 세부 정보를 숨깁니다. 구현 세부 정보를 몰라도 도구를 사용할 수 있습니다. 값은 로컬 컴퓨터의 사용자 프로필 폴더에 있는 JSON 파일에 저장됩니다.

파일 시스템 경로:

%APPDATA%\Microsoft\UserSecrets\<secrets_GUID>\user-jwts.json

JWT 만들기

다음 명령은 로컬 JWT를 만듭니다.

dotnet user-jwts create

이전 명령은 JWT를 만들고 다음과 유사한 JSON으로 프로젝트의 appsettings.Development.json 파일을 업데이트합니다.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "Authentication": {
    "Schemes": {
      "Bearer": {
        "ValidAudiences": [
          "http://localhost:8401",
          "https://localhost:44308",
          "http://localhost:5182",
          "https://localhost:7076"
        ],
        "ValidIssuer": "dotnet-user-jwts"
      }
    }
  }
}

이전 명령에서 만든 JWT 및 ID를 복사합니다. Curl과 같은 도구를 사용하여 /secret를 테스트합니다.

curl -i -H "Authorization: Bearer {token}" https://localhost:{port}/secret

여기서 {token}은 이전에 생성된 JWT입니다.

JWT 보안 정보 표시

다음 명령은 만료, 범위, 역할, 토큰 헤더 및 페이로드 및 압축 토큰을 포함한 JWT 보안 정보를 표시합니다.

dotnet user-jwts print {ID} --show-all

특정 사용자 및 범위에 대한 토큰 만들기

지원되는 만들기 옵션은 이 토픽의 만들기를 참조하세요.

다음 명령은 MyTestUser라는 사용자에 대한 JWT를 만듭니다.

dotnet user-jwts create --name MyTestUser --scope "myapi:secrets"

이전 명령에는 다음과 유사한 출력이 있습니다.

New JWT saved with ID '43e0b748'.
Name: MyTestUser
Scopes: myapi:secrets

Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.{Remaining token deleted}

이전 토큰을 사용하여 다음 코드에서 /secret2 엔드포인트를 테스트할 수 있습니다.

using System.Security.Claims;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();
builder.Services.AddAuthentication("Bearer").AddJwtBearer();

var app = builder.Build();

app.MapGet("/", () => "Hello, World!");
app.MapGet("/secret", (ClaimsPrincipal user) => $"Hello {user.Identity?.Name}. My secret")
    .RequireAuthorization();
app.MapGet("/secret2", () => "This is a different secret!")
    .RequireAuthorization(p => p.RequireClaim("scope", "myapi:secrets"));

app.Run();