Share via


ASP.NET Core용 gRPC의 인증 및 권한 부여

작성자: James Newton-King

샘플 코드 보기 및 다운로드(다운로드 방법)

gRPC 서비스를 호출하는 사용자 인증

ASP.NET Core 인증과 함께 gRPC를 사용하여 각 호출과 사용자를 연결할 수 있습니다.

다음은 gRPC와 ASP.NET Core 인증을 사용하는 Program.cs의 예입니다.

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapGrpcService<GreeterService>();

참고 항목

ASP.NET Core 인증 미들웨어를 등록하는 순서가 중요합니다. UseAuthenticationUseAuthorization은 항상 UseRouting 이후, UseEndpoints 이전에 호출합니다.

호출 중에 앱에서 사용하는 인증 메커니즘을 구성해야 합니다. 인증 구성은 Program.cs에 추가되며, 앱에서 사용하는 인증 메커니즘에 따라 다릅니다.

인증이 설정되면, gRPC 서비스 메서드에서 ServerCallContext를 통해 사용자에 액세스할 수 있습니다.

public override Task<BuyTicketsResponse> BuyTickets(
    BuyTicketsRequest request, ServerCallContext context)
{
    var user = context.GetHttpContext().User;

    // ... access data from ClaimsPrincipal ...
}

전달자 토큰 인증

클라이언트는 인증을 위해 액세스 토큰을 제공할 수 있습니다. 서버는 토큰의 유효성을 검사하고 사용자를 확인하는 데 사용합니다.

서버에서 JWT 전달자 미들웨어를 사용하여 전달자 토큰 인증을 구성합니다.

.NET gRPC 클라이언트에서 Metadata 컬렉션을 사용하여 호출과 함께 토큰을 보낼 수 있습니다. Metadata 컬렉션의 항목은 gRPC 호출을 통해 HTTP 헤더로 전송됩니다.

public bool DoAuthenticatedCall(
    Ticketer.TicketerClient client, string token)
{
    var headers = new Metadata();
    headers.Add("Authorization", $"Bearer {token}");

    var request = new BuyTicketsRequest { Count = 1 };
    var response = await client.BuyTicketsAsync(request, headers);

    return response.Success;
}

다음을 사용하여 전달자 토큰 설정 CallCredentials

gRPC 호출과 함께 토큰을 서비스로 보내는 대신, 채널에 ChannelCredentials를 구성할 수도 있습니다. ChannelCredentialsMetadata를 자동으로 설정하는 방법을 제공하는 CallCredentials를 포함할 수 있습니다.

사용 CallCredentials의 이점:

  • 인증은 채널에서 중앙에서 구성됩니다. 토큰은 gRPC 호출에 수동으로 제공할 필요가 없습니다.
  • 콜백은 CallCredentials.FromInterceptor 비동기적입니다. 호출 자격 증명은 필요한 경우 외부 시스템에서 자격 증명 토큰을 가져올 수 있습니다. 콜백 내의 비동기 메서드는 onAuthInterceptorContextCancellationToken 사용해야 합니다.

참고 항목

CallCredentials는 채널이 TLS로 보호되는 경우에만 적용됩니다. 안전하지 않은 연결을 통해 인증 헤더를 전송할 경우 보안에 영향이 있으므로 프로덕션 환경에서는 삼가야 합니다. 앱은 채널에서 UnsafeUseInsecureChannelCallCredentials를 설정하여 이 동작을 무시하고 항상 CallCredentials를 사용하도록 채널을 구성할 수 있습니다.

다음 예제의 자격 증명은 모든 gRPC 호출과 함께 토큰을 보내도록 채널을 구성합니다.

private static GrpcChannel CreateAuthenticatedChannel(ITokenProvder tokenProvider)
{
    var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
    {
        var token = await tokenProvider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    });

    var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
    });
    return channel;
}

gRPC 클라이언트 팩터리가 포함된 전달자 토큰

gRPC 클라이언트 팩터리는 AddCallCredentials을 사용하여 전달자 토큰을 보내는 클라이언트를 만들 수 있습니다. 이 메서드는 Grpc.Net.ClientFactory 버전 2.46.0 이상에서 사용할 수 있습니다.

AddCallCredentials에 전달된 대리자는 각 gRPC 호출에 대해 실행됩니다.

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

DI(종속성 주입)를 AddCallCredentials와 결합할 수 있습니다. 오버로드는 IServiceProvider를 대리자에게 전달합니다. 그러면 범위가 지정된 임시 서비스를 사용하여 DI에서 생성된 서비스를 가져오는 데 사용할 수 있습니다.

다음이 포함된 앱을 고려하세요.

  • 전달자 토큰을 가져오기 위한 사용자 정의 ITokenProvider. ITokenProvider는 범위가 지정된 수명으로 DI에 등록됩니다.
  • gRPC 클라이언트 팩터리는 gRPC 서비스 및 웹 API 컨트롤러에 삽입되는 클라이언트를 만들도록 구성됩니다.
  • gRPC 호출은 ITokenProvider를 사용하여 전달자 토큰을 가져와야 합니다.
public interface ITokenProvider
{
    Task<string> GetTokenAsync(CancellationToken cancellationToken);
}

public class AppTokenProvider : ITokenProvider
{
    private string _token;

    public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
    {
        if (_token == null)
        {
            // App code to resolve the token here.
        }

        return _token;
    }
}
builder.Services.AddScoped<ITokenProvider, AppTokenProvider>();

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials(async (context, metadata, serviceProvider) =>
    {
        var provider = serviceProvider.GetRequiredService<ITokenProvider>();
        var token = await provider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    }));

앞의 코드가 하는 역할은 다음과 같습니다.

  • ITokenProviderAppTokenProvider를 정의합니다. 이러한 형식은 gRPC 호출에 대한 인증 토큰 해결을 처리합니다.
  • 범위가 지정된 수명 동안 DI를 사용하여 AppTokenProvider 형식을 등록합니다. AppTokenProvider는 범위의 첫 번째 호출만 계산하도록 토큰을 캐시합니다.
  • 클라이언트 팩터리에 GreeterClient 형식을 등록합니다.
  • 이 클라이언트에 대한 AddCallCredentials를 구성합니다. 대리자는 호출이 수행될 때마다 실행되고 ITokenProvider에 의해 반환된 토큰을 메타데이터에 추가합니다.

클라이언트 인증서 인증

클라이언트에서 인증을 위해 클라이언트 인증서를 제공할 수도 있습니다. 인증서 인증은 ASP.NET Core에 도달하기 훨씬 전에 TLS 수준에서 수행됩니다. 요청이 ASP.NET Core에 도달하면 클라이언트 인증서 인증 패키지를 사용하여 인증서를 ClaimsPrincipal로 확인할 수 있습니다.

참고 항목

클라이언트 인증서를 허용하도록 서버를 구성합니다. Kestrel, IIS 및 Azure에서 클라이언트 인증서를 수락하는 방법에 대한 자세한 내용은 ASP.NET Core에서 인증서 인증 구성을 참조하세요.

.NET gRPC 클라이언트에서 클라이언트 인증서는 HttpClientHandler에 추가되며, gRPC 클라이언트를 만드는 데 사용됩니다.

public Ticketer.TicketerClient CreateClientWithCert(
    string baseAddress,
    X509Certificate2 certificate)
{
    // Add client cert to the handler
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(certificate);

    // Create the gRPC channel
    var channel = GrpcChannel.ForAddress(baseAddress, new GrpcChannelOptions
    {
        HttpHandler = handler
    });

    return new Ticketer.TicketerClient(channel);
}

기타 인증 메커니즘

ASP.NET Core를 지원하는 많은 인증 메커니즘은 gRPC에서 작동합니다.

  • Microsoft Entra ID
  • 클라이언트 인증서
  • Identity서버
  • JWT 토큰
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

서버에서 인증을 구성하는 방법에 대한 자세한 내용은 ASP.NET Core 인증을 참조하세요.

인증을 사용하도록 gRPC 클라이언트를 구성하는 방법은 사용하는 인증 메커니즘에 따라 다릅니다. 이전의 전달자 토큰 및 클라이언트 인증서 예제는 gRPC 호출과 함께 인증 메타데이터를 보내도록 gRPC 클라이언트를 구성할 수 있는 몇 가지 방법을 보여 줍니다.

  • 강력한 형식의 gRPC 클라이언트는 HttpClient를 내부적으로 사용합니다. 인증은 HttpClientHandler에서 구성하거나 사용자 지정 HttpMessageHandler 인스턴스를 HttpClient에 추가하여 구성할 수 있습니다.
  • 각 gRPC 호출에는 선택 사항인 CallOptions 인수가 있습니다. 옵션의 헤더 컬렉션을 사용하여 사용자 지정 헤더를 보낼 수 있습니다.

참고 항목

Windows 인증(NTLM/Kerberos/Negotiate)은 gRPC와 함께 사용할 수 없습니다. gRPC에는 HTTP/2가 필요한데, HTTP/2는 Windows 인증을 지원하지 않습니다.

사용자에게 서비스 및 서비스 메서드에 대한 액세스 권한 부여

기본적으로 인증되지 않은 사용자도 서비스의 모든 메서드를 호출할 수 있습니다. 인증을 요구하려면 서비스에 [Authorize] 특성을 적용합니다.

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
}

[Authorize] 특성의 생성자 인수와 속성을 사용하여 특정 권한 부여 정책과 일치하는 사용자로만 액세스를 제한할 수 있습니다. 예를 들어 MyAuthorizationPolicy라는 사용자 지정 권한 부여 정책이 있는 경우 다음 코드를 사용하여 해당 정책과 일치하는 사용자만 서비스에 액세스할 수 있도록 합니다.

[Authorize("MyAuthorizationPolicy")]
public class TicketerService : Ticketer.TicketerBase
{
}

개별 서비스 메서드에도 [Authorize] 특성을 적용할 수 있습니다. 현재 사용자가 메서드 및 클래스에 적용된 정책과 모두 일치하지 않는 경우 호출자에게 오류가 반환됩니다.

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
    public override Task<AvailableTicketsResponse> GetAvailableTickets(
        Empty request, ServerCallContext context)
    {
        // ... buy tickets for the current user ...
    }

    [Authorize("Administrators")]
    public override Task<BuyTicketsResponse> RefundTickets(
        BuyTicketsRequest request, ServerCallContext context)
    {
        // ... refund tickets (something only Administrators can do) ..
    }
}

추가 리소스

샘플 코드 보기 및 다운로드(다운로드 방법)

gRPC 서비스를 호출하는 사용자 인증

ASP.NET Core 인증과 함께 gRPC를 사용하여 각 호출과 사용자를 연결할 수 있습니다.

다음은 gRPC와 ASP.NET Core 인증을 사용하는 Startup.Configure의 예입니다.

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<GreeterService>();
    });
}

참고 항목

ASP.NET Core 인증 미들웨어를 등록하는 순서가 중요합니다. UseAuthenticationUseAuthorization은 항상 UseRouting 이후, UseEndpoints 이전에 호출합니다.

호출 중에 앱에서 사용하는 인증 메커니즘을 구성해야 합니다. 인증 구성은 Startup.ConfigureServices에 추가되며, 앱에서 사용하는 인증 메커니즘에 따라 다릅니다.

인증이 설정되면, gRPC 서비스 메서드에서 ServerCallContext를 통해 사용자에 액세스할 수 있습니다.

public override Task<BuyTicketsResponse> BuyTickets(
    BuyTicketsRequest request, ServerCallContext context)
{
    var user = context.GetHttpContext().User;

    // ... access data from ClaimsPrincipal ...
}

전달자 토큰 인증

클라이언트는 인증을 위해 액세스 토큰을 제공할 수 있습니다. 서버는 토큰의 유효성을 검사하고 사용자를 확인하는 데 사용합니다.

서버에서 JWT 전달자 미들웨어를 사용하여 전달자 토큰 인증을 구성합니다.

.NET gRPC 클라이언트에서 Metadata 컬렉션을 사용하여 호출과 함께 토큰을 보낼 수 있습니다. Metadata 컬렉션의 항목은 gRPC 호출을 통해 HTTP 헤더로 전송됩니다.

public bool DoAuthenticatedCall(
    Ticketer.TicketerClient client, string token)
{
    var headers = new Metadata();
    headers.Add("Authorization", $"Bearer {token}");

    var request = new BuyTicketsRequest { Count = 1 };
    var response = await client.BuyTicketsAsync(request, headers);

    return response.Success;
}

다음을 사용하여 전달자 토큰 설정 CallCredentials

gRPC 호출과 함께 토큰을 서비스로 보내는 대신, 채널에 ChannelCredentials를 구성할 수도 있습니다. ChannelCredentialsMetadata를 자동으로 설정하는 방법을 제공하는 CallCredentials를 포함할 수 있습니다.

사용 CallCredentials의 이점:

  • 인증은 채널에서 중앙에서 구성됩니다. 토큰은 gRPC 호출에 수동으로 제공할 필요가 없습니다.
  • 콜백은 CallCredentials.FromInterceptor 비동기적입니다. 호출 자격 증명은 필요한 경우 외부 시스템에서 자격 증명 토큰을 가져올 수 있습니다. 콜백 내의 비동기 메서드는 onAuthInterceptorContextCancellationToken 사용해야 합니다.

참고 항목

CallCredentials는 채널이 TLS로 보호되는 경우에만 적용됩니다. 안전하지 않은 연결을 통해 인증 헤더를 전송할 경우 보안에 영향이 있으므로 프로덕션 환경에서는 삼가야 합니다. 앱은 채널에서 UnsafeUseInsecureChannelCallCredentials를 설정하여 이 동작을 무시하고 항상 CallCredentials를 사용하도록 채널을 구성할 수 있습니다.

다음 예제의 자격 증명은 모든 gRPC 호출과 함께 토큰을 보내도록 채널을 구성합니다.

private static GrpcChannel CreateAuthenticatedChannel(ITokenProvder tokenProvider)
{
    var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
    {
        var token = await tokenProvider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    });

    var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
    });
    return channel;
}

gRPC 클라이언트 팩터리가 포함된 전달자 토큰

gRPC 클라이언트 팩터리는 AddCallCredentials을 사용하여 전달자 토큰을 보내는 클라이언트를 만들 수 있습니다. 이 메서드는 Grpc.Net.ClientFactory 버전 2.46.0 이상에서 사용할 수 있습니다.

AddCallCredentials에 전달된 대리자는 각 gRPC 호출에 대해 실행됩니다.

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

DI(종속성 주입)를 AddCallCredentials와 결합할 수 있습니다. 오버로드는 IServiceProvider를 대리자에게 전달합니다. 그러면 범위가 지정된 임시 서비스를 사용하여 DI에서 생성된 서비스를 가져오는 데 사용할 수 있습니다.

다음이 포함된 앱을 고려하세요.

  • 전달자 토큰을 가져오기 위한 사용자 정의 ITokenProvider. ITokenProvider는 범위가 지정된 수명으로 DI에 등록됩니다.
  • gRPC 클라이언트 팩터리는 gRPC 서비스 및 웹 API 컨트롤러에 삽입되는 클라이언트를 만들도록 구성됩니다.
  • gRPC 호출은 ITokenProvider를 사용하여 전달자 토큰을 가져와야 합니다.
public interface ITokenProvider
{
    Task<string> GetTokenAsync(CancellationToken cancellationToken);
}

public class AppTokenProvider : ITokenProvider
{
    private string _token;

    public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
    {
        if (_token == null)
        {
            // App code to resolve the token here.
        }

        return _token;
    }
}
services.AddScoped<ITokenProvider, AppTokenProvider>();

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials(async (context, metadata, serviceProvider) =>
    {
        var provider = serviceProvider.GetRequiredService<ITokenProvider>();
        var token = await provider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    }));

앞의 코드가 하는 역할은 다음과 같습니다.

  • ITokenProviderAppTokenProvider를 정의합니다. 이러한 형식은 gRPC 호출에 대한 인증 토큰 해결을 처리합니다.
  • 범위가 지정된 수명 동안 DI를 사용하여 AppTokenProvider 형식을 등록합니다. AppTokenProvider는 범위의 첫 번째 호출만 계산하도록 토큰을 캐시합니다.
  • 클라이언트 팩터리에 GreeterClient 형식을 등록합니다.
  • 이 클라이언트에 대한 AddCallCredentials를 구성합니다. 대리자는 호출이 수행될 때마다 실행되고 ITokenProvider에 의해 반환된 토큰을 메타데이터에 추가합니다.

클라이언트 인증서 인증

클라이언트에서 인증을 위해 클라이언트 인증서를 제공할 수도 있습니다. 인증서 인증은 ASP.NET Core에 도달하기 훨씬 전에 TLS 수준에서 수행됩니다. 요청이 ASP.NET Core에 도달하면 클라이언트 인증서 인증 패키지를 사용하여 인증서를 ClaimsPrincipal로 확인할 수 있습니다.

참고 항목

클라이언트 인증서를 허용하도록 서버를 구성합니다. Kestrel, IIS 및 Azure에서 클라이언트 인증서를 수락하는 방법에 대한 자세한 내용은 ASP.NET Core에서 인증서 인증 구성을 참조하세요.

.NET gRPC 클라이언트에서 클라이언트 인증서는 HttpClientHandler에 추가되며, gRPC 클라이언트를 만드는 데 사용됩니다.

public Ticketer.TicketerClient CreateClientWithCert(
    string baseAddress,
    X509Certificate2 certificate)
{
    // Add client cert to the handler
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(certificate);

    // Create the gRPC channel
    var channel = GrpcChannel.ForAddress(baseAddress, new GrpcChannelOptions
    {
        HttpHandler = handler
    });

    return new Ticketer.TicketerClient(channel);
}

기타 인증 메커니즘

ASP.NET Core를 지원하는 많은 인증 메커니즘은 gRPC에서 작동합니다.

  • Microsoft Entra ID
  • 클라이언트 인증서
  • Identity서버
  • JWT 토큰
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

서버에서 인증을 구성하는 방법에 대한 자세한 내용은 ASP.NET Core 인증을 참조하세요.

인증을 사용하도록 gRPC 클라이언트를 구성하는 방법은 사용하는 인증 메커니즘에 따라 다릅니다. 이전의 전달자 토큰 및 클라이언트 인증서 예제는 gRPC 호출과 함께 인증 메타데이터를 보내도록 gRPC 클라이언트를 구성할 수 있는 몇 가지 방법을 보여 줍니다.

  • 강력한 형식의 gRPC 클라이언트는 HttpClient를 내부적으로 사용합니다. 인증은 HttpClientHandler에서 구성하거나 사용자 지정 HttpMessageHandler 인스턴스를 HttpClient에 추가하여 구성할 수 있습니다.
  • 각 gRPC 호출에는 선택 사항인 CallOptions 인수가 있습니다. 옵션의 헤더 컬렉션을 사용하여 사용자 지정 헤더를 보낼 수 있습니다.

참고 항목

Windows 인증(NTLM/Kerberos/Negotiate)은 gRPC와 함께 사용할 수 없습니다. gRPC에는 HTTP/2가 필요한데, HTTP/2는 Windows 인증을 지원하지 않습니다.

사용자에게 서비스 및 서비스 메서드에 대한 액세스 권한 부여

기본적으로 인증되지 않은 사용자도 서비스의 모든 메서드를 호출할 수 있습니다. 인증을 요구하려면 서비스에 [Authorize] 특성을 적용합니다.

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
}

[Authorize] 특성의 생성자 인수와 속성을 사용하여 특정 권한 부여 정책과 일치하는 사용자로만 액세스를 제한할 수 있습니다. 예를 들어 MyAuthorizationPolicy라는 사용자 지정 권한 부여 정책이 있는 경우 다음 코드를 사용하여 해당 정책과 일치하는 사용자만 서비스에 액세스할 수 있도록 합니다.

[Authorize("MyAuthorizationPolicy")]
public class TicketerService : Ticketer.TicketerBase
{
}

개별 서비스 메서드에도 [Authorize] 특성을 적용할 수 있습니다. 현재 사용자가 메서드 및 클래스에 적용된 정책과 모두 일치하지 않는 경우 호출자에게 오류가 반환됩니다.

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
    public override Task<AvailableTicketsResponse> GetAvailableTickets(
        Empty request, ServerCallContext context)
    {
        // ... buy tickets for the current user ...
    }

    [Authorize("Administrators")]
    public override Task<BuyTicketsResponse> RefundTickets(
        BuyTicketsRequest request, ServerCallContext context)
    {
        // ... refund tickets (something only Administrators can do) ..
    }
}

추가 리소스