Spring-Boot アプリケーションの一部として @GRpcService 対応 Bean を使用して組み込み gRPC サーバーを自動構成して実行します (短いビデオ)
Gradle
ユーザーはプラグインを適用することをお勧めします。
plugins {
id " io.github.lognet.grpc-spring-boot " version ' 5.1.5 '
}
io.github.lognet.grpc-spring-boot gradle プラグインは、プロジェクトのセットアップを大幅に簡素化します。
repositories {
mavenCentral()
// maven { url "https://oss.sonatype.org/content/repositories/snapshots" } // for snapshot builds
}
dependencies {
implementation ' io.github.lognet:grpc-spring-boot-starter:5.1.5 '
}
純粋なgrpc-netty
依存関係を使用する必要がある場合、スターターはデフォルトでio.grpc:grpc-netty-shaded
推移的な依存関係としてプルします。
implementation ( ' io.github.lognet:grpc-spring-boot-starter:5.1.5 ' ) {
exclude group : ' io.grpc ' , module : ' grpc-netty-shaded '
}
implementation ' io.grpc:grpc-netty:1.58.0 ' // (1)
必ずリリースに一致するバージョンをプルしてください。
クラスパス上の両方のライブラリの存在はgrpc.netty-server.on-collision-prefer-shaded-netty
プロパティでもサポートされます。
Spring Boot 依存関係管理プラグインを使用している場合、開始時にコンパイルされたバージョンと同じバージョンがプルされない可能性があり、バイナリの非互換性の問題が発生します。
この場合、使用するgrpc
バージョンを強制的かつ明示的に設定する必要があります (ここでバージョン マトリックスを参照してください)。
configurations . all {
resolutionStrategy . eachDependency { details ->
if ( " io.grpc " . equalsIgnoreCase(details . requested . group)) {
details . useVersion " 1.58.0 "
}
}
}
互換性マトリックスを含むリリースノートはここにあります |
このガイドに従って、 .proto
ファイルからスタブとサーバー インターフェイスを生成します。
Maven を使用している場合は、このリンクを使用してください。
サーバー インターフェイス実装に@org.lognet.springboot.grpc.GRpcService
の注釈を付ける
必要に応じて、 application.yml/properties
でサーバー ポートを構成します。デフォルトのポートは6565
です。
grpc :
port : 6565
ランダムなポートは、ポートを0 に設定することで定義できます。使用されている実際のポートは、実行中のポート (明示的に構成またはランダムに選択) を挿入する int フィールドの@LocalRunningGrpcPort アノテーションを使用して取得できます。 |
オプションでサーバー リフレクションを有効にします (https://github.com/grpc/grpc-java/blob/master/documentation/server-reflection-tutorial.md を参照)。
grpc :
enableReflection : true
必要に応じて、起動フェーズの順序を設定します (デフォルトはInteger.MAX_VALUE
)。
grpc :
start-up-phase : XXX
必要に応じて、サーバーの正常なシャットダウン中に既存の呼び出しが終了するまで待機する秒数を設定します。この間、新しい電話は拒否されます。負の値は、無限の猶予期間に相当します。デフォルト値は0
(待機しないことを意味します) です。
grpc :
shutdownGrace : 30
Netty 固有のサーバー プロパティは、 grpc.netty-server
プレフィックスで指定できます。
grpc.netty-server.xxxx
値の 1 つを構成すると、トランスポートが Netty ベースになるように暗黙的に設定されます。
grpc :
netty-server :
keep-alive-time : 30s (1)
max-inbound-message-size : 10MB (2)
primary-listen-address : 10.10.15.23:0 (3)
additional-listen-addresses :
- 192.168.0.100:6767 (4)
on-collision-prefer-shaded-netty : false (5)
Duration
タイプのプロパティは、ここで説明する文字列値形式で構成できます。
DataSize
タイプのプロパティは、ここで説明されている文字列値で構成できます。
カスタム ポートを使用して外部ネットワーク IP に公開されます。
SocketAddress
タイプのプロパティの文字列値の形式:
host:port
( port
値が 1 未満の場合、ランダムな値を使用します)
host:
(デフォルトの grpc ポート6565
を使用)
事前定義されたポート6767
を使用して内部ネットワーク IP にも公開されます。
依存関係にshaded
ライブラリとpure
netty ライブラリの両方がある場合は、作成する必要があるNettyServerBuilder
タイプを選択します。これはGRpcServerBuilderConfigurer
に渡されるタイプであり (「カスタム gRPC サーバー構成」を参照)、デフォルトはtrue
(つまり、 io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder
、 false
の場合はio.grpc.netty.NettyServerBuilder
) です。
スターターは、テスト目的で使用する必要があるin-process server
もサポートしています。
grpc :
enabled : false (1)
inProcessServerName : myTestServer (2)
デフォルトのサーバー ( NettyServer
) を無効にします。
in-process
サーバーを有効にします。
NettyServer とin-process サーバーの両方を有効にすると、どちらもHealthStatusManager とGRpcServerBuilderConfigurer の同じインスタンスを共有します (「カスタム gRPC サーバー構成」を参照)。 |
grpc-spring-boot-starter-demo
プロジェクトでは、統合テストを含む完全に機能する例を見つけることができます。
.proto
ファイルのサービス定義は次のようになります。
service Greeter {
rpc SayHello ( HelloRequest ) returns ( HelloReply ) {}
}
io.grpc.BindableService
を拡張する、生成されたio.grpc.examples.GreeterGrpc.GreeterImplBase
クラスに注目してください。
必要なのは、サービス実装に@org.lognet.springboot.grpc.GRpcService
のアノテーションを付けることだけです
@ GRpcService
public static class GreeterService extends GreeterGrpc . GreeterImplBase {
@ Override
public void sayHello ( GreeterOuterClass . HelloRequest request , StreamObserver < GreeterOuterClass . HelloReply > responseObserver ) {
final GreeterOuterClass . HelloReply . Builder replyBuilder = GreeterOuterClass . HelloReply . newBuilder (). setMessage ( "Hello " + request . getName ());
responseObserver . onNext ( replyBuilder . build ());
responseObserver . onCompleted ();
}
}
スターターは、 GlobalとPer Service の2 種類のインターセプターの登録をサポートします。
どちらの場合も、インターセプターはio.grpc.ServerInterceptor
インターフェイスを実装する必要があります。
サービスごと
@ GRpcService ( interceptors = { LogInterceptor . class })
public class GreeterService extends GreeterGrpc . GreeterImplBase {
// ommited
}
LogInterceptor
、タイプLogInterceptor
の Bean がある場合はスプリング ファクトリを介してインスタンス化され、それ以外の場合は引数なしのコンストラクターを介してインスタンス化されます。
グローバル
@ GRpcGlobalInterceptor
public class MyInterceptor implements ServerInterceptor {
// ommited
}
Java config Factory メソッドのアノテーションもサポートされています。
@ Configuration
public class MyConfig {
@ Bean
@ GRpcGlobalInterceptor
public ServerInterceptor globalInterceptor (){
return new ServerInterceptor (){
@ Override
public < ReqT , RespT > ServerCall . Listener < ReqT > interceptCall ( ServerCall < ReqT , RespT > call , Metadata headers , ServerCallHandler < ReqT , RespT > next ) {
// your logic here
return next . startCall ( call , headers );
}
};
}
}
特定のサービスには、グローバル インターセプタを無効にする機会もあります。
@ GRpcService ( applyGlobalInterceptors = false )
public class GreeterService extends GreeterGrpc . GreeterImplBase {
// ommited
}
グローバル インターセプタは、Spring の@Ordered
または@Priority
アノテーションを使用して順序付けできます。 Spring の順序セマンティクスに従って、順序の低い値ほど優先順位が高く、インターセプター チェーンで最初に実行されます。
@ GRpcGlobalInterceptor
@ Order ( 10 )
public class A implements ServerInterceptor {
// will be called before B
}
@ GRpcGlobalInterceptor
@ Order ( 20 )
public class B implements ServerInterceptor {
// will be called after A
}
スターターは組み込みインターセプターを使用して、エラー処理、 Spring Security
、 Validation
、およびMetrics
統合を実装します。それらの順序は、以下のプロパティによって制御することもできます。
grpc.recovery.interceptor-order
(エラー処理インターセプター順序、デフォルトはOrdered.HIGHEST_PRECEDENCE
)
grpc.security.auth.interceptor-order
(デフォルトはOrdered.HIGHEST_PRECEDENCE+1
)
grpc.validation.interceptor-order
(デフォルトはOrdered.HIGHEST_PRECEDENCE+10
)
grpc.metrics.interceptor-order
(デフォルトはOrdered.HIGHEST_PRECEDENCE+20
)
これにより、組み込みインターセプターとカスタム インターセプターの希望の順序を設定できるようになります。
読み続けてください!!!さらにあります
grpc インターセプターの仕組みは、呼び出しをインターセプトしてサーバー呼び出しリスナーを返します。これにより、実際のサービス呼び出しハンドラーに転送する前に、リクエスト メッセージもインターセプトできます。
grpc.security.auth.fail-fast
プロパティをfalse
に設定すると、認証/認可が失敗した場合でも、すべてのダウンストリーム インターセプターとすべてのアップストリーム インターセプター (On_Message) が引き続き実行されます。
interceptor_2
securityInterceptor
であると仮定します。
grpc.security.auth.fail-fast=true
(デフォルト) で認証/認可が失敗した場合:
grpc.security.auth.fail-fast=false
で認証/認可が失敗した場合:
これはspring-cloud-sleuth
プロジェクトによってネイティブにサポートされています。
grpc 統合を引き続き調べてください。
org.springframework.boot:spring-boot-starter-actuator
依存関係を含めることで、スターターは次のように分類された gRPC サーバー メトリックを収集します。
method
- gRPC サービス メソッド FQN (完全修飾名)
result
- 応答ステータスコード
address
- サーバーのローカル アドレス ( grpc.netty-server.additional-listen-addresses
プロパティを使用して追加のリッスン アドレスを公開した場合)
選択したエクスポーターを構成すると、 grpc.server.calls
という名前のtimer
が表示されるはずです。
アプリケーション コンテキストでGRpcMetricsTagsContributor
Bean を定義すると、カスタム タグをgrpc.server.calls
タイマーに追加できます。
RequestAwareGRpcMetricsTagsContributor
Bean を使用して、単項呼び出しとストリーミング呼び出しにタグを付けることもできます。
デモはこちら
メトリクスのカーディナリティを爆発させないように、分散を低く保ちます。 |
metric
インターセプターの優先順位がsecurity
インターセプターよりも高く、 grpc.security.auth.fail-fast
false
に設定されている場合は、認証が失敗した場合でもRequestAwareGRpcMetricsTagsContributor
を実行できます。
このケースはこのテストでカバーされます。
インターセプターの注文の章を必ずお読みください。 |
以下の依存関係を必ず含めてください。
implementation "org.springframework.boot:spring-boot-starter-actuator"
implementation "io.micrometer:micrometer-registry-prometheus"
implementation 'org.springframework.boot:spring-boot-starter-web'
構成 :
management :
metrics :
export :
prometheus :
enabled : true
endpoints :
web :
exposure :
include : " * "
標準の/actuator/metrics
および/actuator/prometheus
エンドポイントは、 grpc.server.calls
メトリクスをレンダリングします (ここでデモを参照)。
GRPC廃棄案 |
スターターは、gRPC サービス メッセージの要求/応答を検証するように自動構成できます。構成の詳細については、「メッセージ検証の実装」に進んでください。
スターターは内部的にjava.util.function.Consumer
型の Bean を定義します。これは、 spring-cloud-stream
がクラスパス上にあるときに関数レジストリとして考慮されますが、これは望ましくありません (正確に持っている場合、 spring-cloud-stream
チャネルを自動登録します)アプリケーションコンテキストに Consumer/Supplier/Function Bean が 1 つあるため、このスターターをspring-cloud-stream
と一緒に使用する場合は、すでに 1 つ存在します)。
これによると、実稼働対応アプリケーションではspring.cloud.function.definition
プロパティを使用し、自動検出に依存しないことが推奨されます。
GRPC Kafka Stream のデモを参照してください。重要な部分はこの行です。
スターターは、Spring Security フレームワークとの統合を活用してユーザーを認証および認可するための組み込みサポートを提供します。
サポートされている認証プロバイダーと構成オプションの詳細については、Spring Security Integration のセクションを参照してください。
トランスポート セキュリティは、ルート証明書とその秘密キー パスを使用して構成できます。
grpc :
security :
cert-chain : classpath:cert/server-cert.pem
private-key : file:../grpc-spring-boot-starter-demo/src/test/resources/cert/server-key.pem
両方のプロパティの値は、ResourceEditor でサポートされている形式です。
クライアント側はそれに応じて構成する必要があります。
(( NettyChannelBuilder ) channelBuilder )
. useTransportSecurity ()
. sslContext ( GrpcSslContexts . forClient (). trustManager ( certChain ). build ());
このスターターは、SSL をサポートするためにデフォルトでio.netty:netty-tcnative-boringssl-static
依存関係をプルします。
別の SSL/TLS サポートが必要な場合は、この依存関係を除外し、セキュリティ ガイドに従ってください。
セキュリティ設定にさらに詳細な調整が必要な場合は、「カスタム gRPC サーバー構成」で説明されているカスタム コンフィギュラーを使用してください。 |
io.grpc.Server
構築に使用されるio.grpc.ServerBuilder
インスタンスをインターセプトするには、 org.lognet.springboot.grpc.GRpcServerBuilderConfigurer
から継承する Bean をコンテキストに追加し、 configure
メソッドをオーバーライドします。
複数のコンフィギュラーもサポートされています。
configure
メソッドを呼び出すまでに、インターセプターを含む、検出されたすべてのサービスが、渡されたビルダーに追加されていました。
configure
メソッドの実装では、カスタム構成を追加できます。
@ Component
public class MyGRpcServerBuilderConfigurer extends GRpcServerBuilderConfigurer {
@ Override
public void configure ( ServerBuilder <?> serverBuilder ){
serverBuilder
. executor ( YOUR EXECUTOR INSTANCE )
. useTransportSecurity ( YOUR TRANSPORT SECURITY SETTINGS );
(( NettyServerBuilder ) serverBuilder ) // cast to NettyServerBuilder (which is the default server) for further customization
. sslContext ( GrpcSslContexts // security fine tuning
. forServer (...)
. trustManager (...)
. build ())
. maxConnectionAge (...)
. maxConnectionAgeGrace (...);
}
};
}
@ Component
public class MyCustomCompressionGRpcServerBuilderConfigurer extends GRpcServerBuilderConfigurer {
@ Override
public void configure ( ServerBuilder <?> serverBuilder ){
serverBuilder
. compressorRegistry ( YOUR COMPRESSION REGISTRY )
. decompressorRegistry ( YOUR DECOMPRESSION REGISTRY ) ;
}
};
}
NettyServer とin-process サーバーの両方を有効にすると、 configure メソッドはコンフィギュラーの同じインスタンスで呼び出されます。渡された serverBuilder を区別する必要がある場合は、タイプを確認できます。これが現在の制限です。 |
GRpcServerInitializedEvent
はサーバーの起動時に発行され、通常の Spring API を使用して使用できます。
バージョン5.1.0
以降、 spring-boot-starter-gradle-plugin は SalesForce の reactive-grpc プロトコル プラグインを統合します。
import org.lognet.springboot.grpc.gradle.ReactiveFeature
plugins {
id " io.github.lognet.grpc-spring-boot "
}
grpcSpringBoot {
reactiveFeature . set( ReactiveFeature . REACTOR ) // or ReactiveFeature.RX
}
テストとリアクティブ grpc サンプル サービスは次のとおりです。
スターターは、サービスによってスローされた例外をエラー ハンドラーに伝播する役割を担うGRpcExceptionHandlerInterceptor
を登録します。
エラー処理メソッドは、 @GRpcServiceAdvice
アノテーション付き Bean と@GRpcExceptionHandler
アノテーション付きメソッドを使用することで登録できます。
これらはglobal
エラー ハンドラーとみなされ、型階層によってスローされた例外に最も近い例外型パラメーターを持つメソッドが呼び出されます。
エラー ハンドラーの署名は、次のパターンに従う必要があります。
戻り値の型 | パラメータ1 | パラメータ2 |
---|---|---|
io.grpc.ステータス |
| GRpcException スコープ |
@ GRpcServiceAdvice
class MyHandler1 {
@ GRpcExceptionHandler
public Status handle ( MyCustomExcpetion exc , GRpcExceptionScope scope ){
}
@ GRpcExceptionHandler
public Status handle ( IllegalArgumentException exc , GRpcExceptionScope scope ){
}
}
@ GRpcServiceAdvice
class MyHandler2 {
@ GRpcExceptionHandler
public Status anotherHandler ( NullPointerException npe , GRpcExceptionScope scope ){
}
}
advice
Bean とハンドラー メソッドは、相互に干渉せず、処理される例外の型があいまいにならない限り、必要な数だけ持つことができます。
grpc
サービス Bean はエラー ハンドラー用にも検出され、 @GRpcServiceAdvice
Bean で検出されたグローバル エラー処理メソッドよりも高い優先順位を持ちます。サービスレベルのエラー処理メソッドはprivate
とみなされ、このサービスによって例外がスローされた場合にのみ呼び出されます。
class SomeException extends Exception {
}
class SomeRuntimeException extends RuntimeException {
}
@ GRpcService
public class HelloService extends GreeterGrpc . GreeterImplBase {
@ Override
public void sayHello ( GreeterOuterClass . HelloRequest request , StreamObserver < GreeterOuterClass . HelloReply > responseObserver ) {
...
throw new GRpcRuntimeExceptionWrapper ( new SomeException ()) ; // (1)
//or
throw new GRpcRuntimeExceptionWrapper ( new SomeException (), "myHint" ); // (2)
//or
throw new SomeRuntimeException (); //(3)
}
@ GRpcExceptionHandler
public Status privateHandler ( SomeException npe , GRpcExceptionScope scope ){
// INVOKED when thrown from HelloService service
String myHint = scope . getHintAs ( String . class ); // (4)
scope . getResponseHeaders (). put ( Metadata . Key . of ( "custom" , Metadata . ASCII_STRING_MARSHALLER ), "Value" ); // (5)
}
@ GRpcExceptionHandler
public Status privateHandler ( SomeRuntimeException npe , GRpcExceptionScope scope ){
// INVOKED when thrown from HelloService service
}
}
@ GRpcServiceAdvice
class MyHandler {
@ GRpcExceptionHandler
public Status anotherHandler ( SomeException npe , GRpcExceptionScope scope ){
// NOT INVOKED when thrown from HelloService service
}
@ GRpcExceptionHandler
public Status anotherHandler ( SomeRuntimeException npe , GRpcExceptionScope scope ){
// NOT INVOKED when thrown from HelloService service
}
}
grpc
サービス API の性質上、チェック例外のスローが許可されていないため、チェック例外をラップするために特別なランタイム例外タイプが提供されています。その後、ハンドラー メソッドを探すときにラップが解除されます。
GRpcRuntimeExceptionWrapper
例外をスローするときに、 handler
メソッドのscope
オブジェクトからアクセスできるhint
オブジェクトを渡すこともできます。
ランタイム例外はそのままスローできるため、ラップする必要はありません。
ヒントオブジェクトを取得します。
カスタムヘッダーをクライアントに送信します。
認証の失敗はAuthenticationException
を介して伝播され、認可の失敗はAccessDeniedException
を介して伝播されます。
検証の失敗は、 ConstraintViolationException
を介して伝播されます。失敗したリクエストの場合は、ヒントとしてStatus.INVALID_ARGUMENT
が使用され、失敗した応答の場合は、ヒントとしてStatus.FAILED_PRECONDITION
が使用されます。
デモはこちら
XML デプロイメント記述子を介した Bean Validation 構成サポートのおかげで、生成されたメッセージをカスタムprotoc
コンパイラで計測する代わりに、XML を介して生成されたクラスに制約を提供することができます。
org.springframework.boot:spring-boot-starter-validation
関係をプロジェクトに追加します。
META-INF/validation.xml
および制約宣言ファイルを作成します。 (IntelliJ IDEA は、Bean 検証制約 XML ファイルを承認するための優れたオートコンプリート サポートを備えています)
Hibernate
バリデーターのドキュメントのサンプルも参照してください。
デモ構成と対応するテストはここで見つけることができます
request
とresponse
メッセージの両方が検証されることに注意してください。
gRPC メソッドが同じ要求メッセージ タイプと応答メッセージ タイプを使用する場合、 org.lognet.springboot.grpc.validation.group.RequestMessage
検証グループとorg.lognet.springboot.grpc.validation.group.ResponseMessage
検証グループを使用して、異なる検証ロジックを適用できます。 :
...
< getter name = " someField " >
<!-- should be empty for request message -->
< constraint annotation = " javax.validation.constraints.Size " >
< groups >
< value >org.lognet.springboot.grpc.validation.group.RequestMessage</ value > (1)
</ groups >
< element name = " min " >0</ element >
< element name = " max " >0</ element >
</ constraint >
<!-- should NOT be empty for response message -->
< constraint annotation = " javax.validation.constraints.NotEmpty " >
< groups >
< value >org.lognet.springboot.grpc.validation.group.ResponseMessage</ value > (2)
</ groups >
</ constraint >
</ getter >
...
この制約はrequest
メッセージにのみ適用します
この制約はresponse
メッセージにのみ適用されます
カスタムのクロスフィールド制約とその使用法にも注意してください。
< bean class = " io.grpc.examples.GreeterOuterClass$Person " >
< class >
< constraint annotation = " org.lognet.springboot.grpc.demo.PersonConstraint " />
</ class >
<!-- ... -->
</ bean >
インターセプターの順序付けの章で説明されているように、 validation
インターセプターにsecurity
インターセプターより高い優先順位を与え、 grpc.security.auth.fail-fast
プロパティをfalse
に設定できます。
このシナリオでは、呼び出しが認証されておらず無効である場合、クライアントはStatus.PERMISSION_DENIED/Status.UNAUTHENTICATED
応答ステータスの代わりにStatus.INVALID_ARGUMENT
を取得します。デモはこちら
rpc メソッドに@Transactional
(デフォルトで有効になっていない場合はspring.aop.proxy-target-class=true
) のアノテーションを付けることはまだ可能ですが、予期しない動作が発生する可能性があります。以下の grpc メソッドの実装を考えてみましょう。
@ GRpcService
class MyGrpcService extends ...{
@ Autowired
private MyJpaRepository repo ;
@ Transactional //(1)
public void rpcCall ( Req request , StreamOvserver < Res > observer ) {
Res response = // Database operations via repo
observer . onNext ( response ); //(2)
observer . onCompleted ();
} //(3)
}
メソッドには@Transactional
という注釈が付けられており、Spring はメソッドが返された後のある時点でトランザクションをコミットします。
呼び出し元に応答が返されます
メソッドが戻り、トランザクションは最終的にコミットされます。
理論的には、ご覧のとおり、実際には、クライアント (ネットワーク遅延が最小限で、grpc サーバーが <2> の直後にコンテキストの切り替えを推奨した場合) が、その前に別の grpc 呼び出しを介してデータベースにアクセスしようとするまでの時間は短くなります。トランザクションはコミットされます。
この状況を克服する解決策は、トランザクション ロジックを別のサービス クラスに外部化することです。
@ Service
class MyService {
@ Autowired
private MyJpaRepository repo ;
@ Transactional //(1)
public Res doTransactionalWork (){
// Database operations via repo
return result ;
} //(2)
}
@ GRpcService
class MyGrpcService extends ...{
@ Autowired
private MyService myService ;
public void rpcCall ( Req request , StreamOvserver < Res > observer ) {
Res response = myService . doTransactionalWork ();
observer . onNext ( response ); //(3)
observer . onCompleted ();
}
}
サービスメソッドはトランザクションです
トランザクションは最終的にコミットされます。
トランザクションがコミットされた後に返信します。
このアプローチに従うことで、トランスポート層とビジネス ロジックも分離され、個別にテストできるようになります。
スキーム | 依存関係 |
---|---|
基本 |
|
ベアラー |
|
カスタム |
|
GRPC セキュリティ構成は、Spring WEB セキュリティ構成と同じプリンシパルおよび API に従います。クラスパスにorg.springframework.security:spring-security-config
依存関係がある場合、デフォルトで有効になります。
サービス/メソッドで@Secured
アノテーションを使用してエンドポイントを保護するか、API を使用してデフォルトをオーバーライドする ( @Secured
アノテーションを処理します) ことができます。
@ Configuration
class MySecurityCfg extends GrpcSecurityConfigurerAdapter {
@ Override
public void configure ( GrpcSecurity builder ) throws Exception {
MethodsDescriptor <?,?> adminMethods = MyServiceGrpc . getSomeMethod ();
builder
. authorizeRequests ()
. methods ( adminMethods ). hasAnyRole ( "admin" )
. anyMethodExcluding ( adminMethods ). hasAnyRole ( "user" )
. withSecuredAnnotation ();( 1 )
}
}
またはAPI
と@Secured
アノテーションを組み合わせます。
このデフォルト構成は、 org.springframework.security.access.annotation.@Secured
アノテーションが付けられた GRPC メソッド/サービスを保護します。
注釈の値を空のままにする ( @Secured({})
) ことは、 authenticate
のみを行い、承認は実行されないことを意味します。
JwtDecoder
Bean がコンテキスト内に存在する場合、認証要求の検証を処理するためにJwtAuthenticationProvider
も登録されます。
BasicAuthSchemeSelector
とBearerTokenAuthSchemeSelector
も自動的に登録され、ユーザー名/パスワードとベアラー トークンによる認証をサポートします。
grpc.security.auth.enabled
をfalse
に設定すると、GRPC セキュリティをオフにできます。
GRPC セキュリティ構成のカスタマイズは、 GrpcSecurityConfigurerAdapter
拡張することで行われます (さまざまな構成例とテスト シナリオはここにあります)。
@ Configuration
public class GrpcSecurityConfiguration extends GrpcSecurityConfigurerAdapter {
@ Autowired
private JwtDecoder jwtDecoder ;
@ Override
public void configure ( GrpcSecurity builder ) throws Exception {
builder . authorizeRequests ()( 1 )
. methods ( GreeterGrpc . getSayHelloMethod ()). hasAnyAuthority ( "SCOPE_profile" )( 2 )
. and ()
. authenticationProvider ( JwtAuthProviderFactory . withAuthorities ( jwtDecoder ));( 3 )
}
}
認可設定オブジェクトの取得
sayHello
メソッドのMethodDefinition
は、 SCOPE_profile
権限を持つ認証済みユーザーに許可されます。
JwtAuthenticationProvider
を使用して、 spring.security.oauth2.resourceserver.jwt.issuer-uri
プロパティで構成されたリソース サーバーに対してユーザー クレーム ( BEARER
トークン) を検証します。
AuthenticationSchemeSelector
インターフェイスを実装することで、独自のカスタム認証プロバイダーをプラグインすることができます。
@ Configuration
public class GrpcSecurityConfiguration extends GrpcSecurityConfigurerAdapter {
@ Override
public void configure ( GrpcSecurity builder ) throws Exception {
builder . authorizeRequests ()
. anyMethod (). authenticated () //(1)
. and ()
. authenticationSchemeSelector ( new AuthenticationSchemeSelector () { //(2)
@ Override
public Optional < Authentication > getAuthScheme ( CharSequence authorization ) {
return new MyAuthenticationObject (); // (3)
}
})
. authenticationProvider ( new AuthenticationProvider () { // (4)
@ Override
public Authentication authenticate ( Authentication authentication ) throws AuthenticationException {
MyAuthenticationObject myAuth = ( MyAuthenticationObject ) authentication ;
//validate myAuth
return MyValidatedAuthenticationObject ( withAuthorities ); //(5)
}
@ Override
public boolean supports ( Class <?> authentication ) {
return MyAuthenticationObject . class . isInstance ( authentication );
}
});
}
}
すべてのサービスメソッドを保護します。
独自のAuthenticationSchemeSelector
を登録します。
提供された認証ヘッダーに基づいて、 Authentication
オブジェクトをクレームとして返します (まだ認証されていません)
MyAuthenticationObject
の検証をサポートする独自のAuthenticationProvider
を登録します。
提供されたauthentication
を検証し、検証および認証されたAuthentication
オブジェクトを返します。
AuthenticationSchemeSelector
アプリケーション コンテキストで Spring Bean を定義することによって登録することもできます。
@ Bean
public AuthenticationSchemeSelector myCustomSchemeSelector (){
return authHeader ->{
// your logic here
};
}
クライアント側の構成サポートのセクションでは、カスタム認証スキームと GRPC クライアントからの要求を渡す方法について説明します。
バージョン4.5.9
以降では、grpc サービス メソッドおよび grpc サービス タイプで標準の@PreAuthorize
および@PostAuthorize
アノテーションを使用することもできます。
コールタイプ | 入力オブジェクト参照 | 出力オブジェクト参照 | サンプル |
---|---|---|---|
単項 | パラメータ名別 |
| @ Override
@ PreAuthorize ( "#person.age<12" )
@ PostAuthorize ( "returnObject.description.length()>0" )
public void unary ( Person person , StreamObserver < Assignment > responseObserver ) {
} |
入力ストリーム、 |
| | @ Override
@ PreAuthorize ( "#p0.getAge()<12" )
@ PostAuthorize ( "returnObject.description.length()>0" )
public StreamObserver < Person > inStream ( StreamObserver < Assignment > responseObserver ) {
} |
単一のリクエスト、 | パラメータ名別 |
| @ Override
@ PreAuthorize ( "#person.age<12" )
@ PostAuthorize ( "returnObject.description.length()>0" )
public void outStream ( Person person , StreamObserver < Assignment > responseObserver ) {
} |
ビディストリーム |
| | @ Override
@ PreAuthorize ( "#p0.age<12" )
@ PostAuthorize ( "returnObject.description.length()>0" )
public StreamObserver < Person > bidiStream ( StreamObserver < Assignment > responseObserver ) {
} |
セキュリティで保護されたメソッドの実装でAuthentication
オブジェクトを取得するには、以下のスニペットを使用してください
final Authentication auth = GrpcSecurity . AUTHENTICATION_CONTEXT_KEY . get ();
4.5.6
以降、 Authentication
オブジェクトは標準の Spring API 経由でも取得できるようになりました。
final Authentication auth = SecurityContextHolder . getContext (). getAuthentication ();
io.github.lognet:grpc-client-spring-boot-starter
依存関係をJava grpc クライアントアプリケーションに追加することで、チャネルごとまたは呼び出しごとの資格情報を簡単に構成できます。
class MyClient {
public void doWork (){
final AuthClientInterceptor clientInterceptor = new AuthClientInterceptor (( 1 )
AuthHeader . builder ()
. bearer ()
. binaryFormat ( true )( 3 )
. tokenSupplier ( this :: generateToken )( 4 )
);
Channel authenticatedChannel = ClientInterceptors . intercept (
ManagedChannelBuilder . forAddress ( "host" , 6565 ). build (), clientInterceptor ( 2 )
);
// use authenticatedChannel to invoke GRPC service
}
private ByteBuffer generateToken (){ ( 4 )
// generate bearer token against your resource server
}
}
クライアントインターセプターの作成
インターセプトチャネル
バイナリ形式をオン/オフにします。
true
の場合、認証ヘッダーはバイナリ マーシャラーを使用してAuthorization-bin
キーとともに送信されます。
false
の場合、認証ヘッダーは ASCII マーシャラーを使用してAuthorization
キーとともに送信されます。
トークン生成機能の提供(例を参照してください。)
class MyClient {
public void doWork (){
AuthCallCredentials callCredentials = new AuthCallCredentials ( ( 1 )
AuthHeader . builder (). basic ( "user" , "pwd" . getBytes ())
);
final SecuredGreeterGrpc . SecuredGreeterBlockingStub securedFutureStub = SecuredGreeterGrpc . newBlockingStub ( ManagedChannelBuilder . forAddress ( "host" , 6565 ));( 2 )
final String reply = securedFutureStub
. withCallCredentials ( callCredentials )( 3 )
. sayAuthHello ( Empty . getDefaultInstance ()). getMessage ();
}
}
基本的なスキームを使用して通話資格情報を作成する
サービススタブの作成
通話認証情報を通話に添付する
AuthHeader
オーダーメイドの認証スキームを使用して構築することもできます。
AuthHeader
. builder ()
. authScheme ( "myCustomAuthScheme" )
. tokenSupplier (()-> generateMyCustomToken ())
スターターは、HealthServiceImpl のデフォルト実装を登録します。
アプリケーション コンテキストに ManagedHealthStatusService Bean を登録することで、独自の Bean を提供できます。
クラスパスにorg.springframework.boot:spring-boot-starter-actuator
とorg.springframework.boot:spring-boot-starter-web
がある場合、スターターは以下を公開します。
/actuator/health
エンドポイントの下のgrpc
ヘルス インジケーター。
/actuator/grpc
エンドポイント。
これは、標準のエンドポイントと正常性構成によって制御できます。
バージョン3.3.0
以降、 org.springframework.cloud:spring-cloud-starter-consul-discovery
がクラスパスにあり、 spring.cloud.service-registry.auto-registration.enabled
にある場合、スターターは実行中の grpc サーバーを Consul レジストリに自動登録します。 spring.cloud.service-registry.auto-registration.enabled
はfalse
に設定されていません。
組み込みのGrpc
とWeb
サーバーの両方を実行することを選択した場合、標準の登録 Web サービス名に干渉しないように、登録されたサービス名にはgrpc-
、つまりgrpc-${spring.application.name}
というプレフィックスが付けられます。
ConsulDiscoveryProperties
、 spring.cloud.consul.discovery
という接頭辞が付いた構成プロパティからバインドされ、値はgrpc.consul.discovery
接頭辞が付いたプロパティ (設定されている場合) によって上書きされます。これにより、アプリケーションから両方を公開することを選択した場合、 rest
サービスとgrpc
サービスに対して個別の Consul Discovery 構成を使用できるようになります。
spring :
cloud :
consul :
discovery :
metadata :
myKey : myValue (1)
tags :
- myWebTag (2)
grpc :
consul :
discovery :
tags :
- myGrpcTag (3)
rest
サービスとgrpc
サービスは両方ともメタデータmyKey=myValue
で登録されます
残りのサービスはmyWebTag
に登録されます
Grpc サービスはmyGrpcTag
に登録されています
spring.cloud.consul.discovery.register-health-check
(またはgrpc.consul.discovery.register-health-check
) をtrue
に設定すると、GRPC ヘルス チェック サービスが Consul に登録されます。
サポートされている登録モードは 4 つあります。
SINGLE_SERVER_WITH_GLOBAL_CHECK
(デフォルト)
このモードでは、実行中の grpc サーバーは、空のserviceId
を持つ単一のgrpc
チェックを持つ単一のサービスとして登録されます。
デフォルトの実装では何もせず、単にSERVING
ステータスを返すだけであることに注意してください。このモードにカスタムのヘルス チェック実装を提供することもできます。
SINGLE_SERVER_WITH_CHECK_PER_SERVICE
このモードでは、実行中の grpc サーバーが単一のサービスとして登録され、検出されたgrpc
サービスごとにチェックが行われます。
STANDALONE_SERVICES
このモードでは、検出された各 grpc サービスが 1 回のチェックで 1 つのサービスとして登録されます。登録された各サービスには、独自のサービス名がタグ付けされます。
NOOP
- grpc サービスが登録されていません。このモードは、アプリケーションでrest
サービスとgrpc
サービスの両方を提供する場合に便利ですが、何らかの理由でrest
サービスのみを Consul に登録する必要があります。
grpc :
consule :
registration-mode : SINGLE_SERVER_WITH_CHECK_PER_SERVICE
本番環境に対応したサービスを構築する場合、サーバー側とクライアント側の両方で使用するプロト生成クラスのみを保持するサービス gRPC API 用に別のプロジェクトを作成することをお勧めします。
次に、このプロジェクトをimplementation
依存関係としてgRPC client
とgRPC server
プロジェクトに追加します。
Eureka
統合するには、Spring の優れたガイドに従うだけです。
以下は、サーバー プロジェクトとクライアント プロジェクトの両方の構成の重要な部分です。
eureka starter を、 proto
ファイルから生成されたクラスとともにサーバー プロジェクトの依存関係として追加します。
dependencies {
implementation( ' org.springframework.cloud:spring-cloud-starter-eureka ' )
implementation project( " :yourProject-api " )
}
gRPC サーバー自体を Eureka に登録するように構成します。
spring :
application :
name : my-service-name (1)
Eureka のServiceId
、デフォルトでは Spring アプリケーション名であり、サービスがそれ自体を Eureka に登録する前に指定します。
grpc :
port : 6565 (1)
eureka :
instance :
nonSecurePort : ${grpc.port} (2)
client :
serviceUrl :
defaultZone : http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ (3)
gRPC がリッスンしているポート番号を指定します。
eureka サービス ポートをgrpc.port
と同じになるように登録し、クライアントがリクエストの送信先を認識できるようにします。
サービスが自身を登録するレジストリ URL を指定します。
gRPC サービスを Spring Boot アプリケーションの一部として公開します。
@ SpringBootApplication
@ EnableEurekaClient
public class EurekaGrpcServiceApp {
@ GRpcService
public static class GreeterService extends GreeterGrpc . GreeterImplBase {
@ Override
public void sayHello ( GreeterOuterClass . HelloRequest request , StreamObserver < GreeterOuterClass . HelloReply > responseObserver ) {
}
}
public static void main ( String [] args ) {
SpringApplication . run ( DemoApp . class , args );
}
}
eureka スターターを、 proto
ファイルから生成されたクラスとともにクライアント プロジェクトの依存関係として追加します。
dependencies {
implementation( ' org.springframework.cloud:spring-cloud-starter-eureka ' )
implementation project( " :yourProject-api " )
}
eureka サービス レジストリを見つけるようにクライアントを構成します。
eureka :
client :
register-with-eureka : false (1)
service-url :
defaultZone : http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ (2)
このプロジェクトが別のクライアントへのサービスとして機能することを意図していない場合はfalse
。
レジストリ URL を指定すると、このクライアントは必要なサービスを検索する場所がわかります。
@ EnableEurekaClient
@ SpringBootApplication
public class GreeterServiceConsumerApplication {
public static void main ( String [] args ) {
SpringApplication . run ( GreeterServiceConsumerApplication . class , args );
}
}
EurekaClient を使用して、Eureka から gRPC サービス インスタンスの座標を取得し、サービスを利用します。
@ EnableEurekaClient
@ Component
public class GreeterServiceConsumer {
@ Autowired
private EurekaClient client ;
public void greet ( String name ) {
final InstanceInfo instanceInfo = client . getNextServerFromEureka ( "my-service-name" , false ); //(1)
final ManagedChannel channel = ManagedChannelBuilder . forAddress ( instanceInfo . getIPAddr (), instanceInfo . getPort ())
. usePlaintext ()
. build (); //(2)
final GreeterServiceGrpc . GreeterServiceFutureStub stub = GreeterServiceGrpc . newFutureStub ( channel ); //(3)
stub . greet ( name ); //(4)
}
}
my-service-name
インスタンスに関する情報を取得します。
それに応じてchannel
構築します。
channel
を使用してスタブを作成します。
サービスを呼び出します。
アパッチ2.0