🗂
【Go】gRPCの認証Inerceptorで取得した情報を後続処理で使用する
概要
gRPCで特定のサービスに対し、認証処理を挟みたい場合は、Inerceptorを使うことが多いと思います。実装の概要等はgRPC-GoのMiddleware (Inerceptor) で認証/認可してみるの記事が参考になると思います。
今回はこのInerceptorで認証時に取得した情報を、後続で使いたい場合どうするかというのを書きたいと思います。
前提など
- 今回はJwtトークンを検証する方法での認証とします。Jwtトークンの中に含めているuserIdを、後続処理に引き渡します。
- Contextを介して、取得したデータを後続に引き渡します。
実装サンプル
まずはmainのコードです。grpcwebで起動していますが、別に使う必然性はないです。
main.go
func main() {
// 後述の認証用Inerceptorを設定
grpcServer := grpc.NewServer(
grpc.UnaryInterceptor(
grpc_middleware.ChainUnaryServer(
service.AuthInterceptor,
),
),
)
// サービスの登録
pb.RegisterAuthenticationUserServiceServer(grpcServer, service.NewAuthenticationUserService(dbEngine))
pb.RegisterGeographicPointServiceServer(grpcServer, service.NewGeographicPointService(dbEngine))
// grpcwebでの起動
wrappedServer := grpcweb.WrapServer(
grpcServer,
grpcweb.WithOriginFunc(func(origin string) bool {
return origin == os.Getenv("VIEW_DOMAIN")
}),
)
mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(wrappedServer.ServeHTTP))
hs := &http.Server{
Addr: ":8080",
Handler: mux,
}
log.Fatal(hs.ListenAndServe())
}
以下が認証用のInerceptorです。後続でuserIdを使用できるように、 Contextにセットしてreturnしています。
AuthInterceptor.go
func AuthInterceptor(ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
// 認証をスキップするパス
if info.FullMethod == "/pb.AuthenticationUserService/Login" {
return handler(ctx, req)
} else {
// 認証がOKならContextを返す
userIdSetCtx, err := authorize(ctx)
if err != nil {
return nil, err
}
return handler(userIdSetCtx, req)
}
}
// トークンから情報を取得してContextを返す
func authorize(ctx context.Context) (context.Context, error) {
// ヘッダーのトークンからユーザIDを取得
token, err := grpc_auth.AuthFromMD(ctx, "Bearer")
if err != nil {
return nil, err
}
// jwtのトークンを検証してuserIdを取得(処理の記載は割愛)
userId, err := GetUserIdInfoFromJwtToken(token)
if err != nil {
return nil, err
}
// contextにユーザIDをセット
return context.WithValue(ctx, "userId", userId)
}
以下がサービスでuserIdを使用する箇所です。Contextから取得します。
geographicPointService.go
func (s *GeographicPointService) AddGeographicPoint(ctx context.Context, r *pb.AddGeographicPointRequest) (*pb.RegsiterGeographicPointResponse, error) {
if ctx.Err() == context.Canceled {
return &pb.RegsiterGeographicPointResponse{}, fmt.Errorf("client cancelled: abandoning")
}
// contextからユーザID取得
userId := ctx.Value("userId").(string)
// 後続処理は記載割愛・・
・
・
・
}
Discussion