补充grpc认证

This commit is contained in:
yumaojun03 2025-08-24 14:51:13 +08:00
parent afa7743e82
commit 4a0ed93617
4 changed files with 154 additions and 2 deletions

View File

@ -0,0 +1,56 @@
package hello_service
import context "context"
// PerRPCCredentials defines the common interface for the credentials which need to
// attach security information to every RPC (e.g., oauth2).
// type PerRPCCredentials interface {
// // GetRequestMetadata gets the current request metadata, refreshing tokens
// // if required. This should be called by the transport layer on each
// // request, and the data should be populated in headers or other
// // context. If a status code is returned, it will be used as the status for
// // the RPC (restricted to an allowable set of codes as defined by gRFC
// // A54). uri is the URI of the entry point for the request. When supported
// // by the underlying implementation, ctx can be used for timeout and
// // cancellation. Additionally, RequestInfo data will be available via ctx
// // to this call. TODO(zhaoq): Define the set of the qualified keys instead
// // of leaving it as an arbitrary string.
// GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
// // RequireTransportSecurity indicates whether the credentials requires
// // transport security.
// RequireTransportSecurity() bool
// }
func NewClientAuthentication(clientId, clientSecret string) *Authentication {
return &Authentication{
clientID: clientId,
clientSecret: clientSecret,
}
}
// Authentication todo
type Authentication struct {
clientID string
clientSecret string
}
// WithClientCredentials todo
func (a *Authentication) WithClientCredentials(clientID, clientSecret string) {
a.clientID = clientID
a.clientSecret = clientSecret
}
// GetRequestMetadata todo
func (a *Authentication) GetRequestMetadata(context.Context, ...string) (
map[string]string, error,
) {
return map[string]string{
ClientHeaderKey: a.clientID,
ClientSecretKey: a.clientSecret,
}, nil
}
// RequireTransportSecurity todo
func (a *Authentication) RequireTransportSecurity() bool {
return false
}

View File

@ -0,0 +1,90 @@
package hello_service
import (
context "context"
"fmt"
"github.com/infraboard/mcube/v2/ioc/config/log"
"github.com/rs/zerolog"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
status "google.golang.org/grpc/status"
)
const (
ClientHeaderKey = "client-id"
ClientSecretKey = "client-secret"
)
// GrpcAuthUnaryServerInterceptor returns a new unary server interceptor for auth.
func GrpcAuthUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return newGrpcAuther().Auth
}
func newGrpcAuther() *grpcAuther {
return &grpcAuther{
log: log.Sub("Grpc Auther"),
}
}
// UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info
// contains all the information of this RPC the interceptor can operate on. And handler is the wrapper
// of the service method implementation. It is the responsibility of the interceptor to invoke handler
// to complete the RPC.
// type UnaryServerInterceptor func(ctx context.Context, req any, info *UnaryServerInfo, handler UnaryHandler) (resp any, err error)
// internal todo
type grpcAuther struct {
log *zerolog.Logger
}
func (a *grpcAuther) Auth(
ctx context.Context, req any,
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (resp any, err error) {
// http2 header -> metadata
// 重上下文中获取认证信息
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, fmt.Errorf("ctx is not an grpc incoming context")
}
fmt.Println("gprc header info: ", md)
clientId, clientSecret := a.GetClientCredentialsFromMeta(md)
// 校验调用的客户端凭证是否有效
if err := a.validateServiceCredential(clientId, clientSecret); err != nil {
return nil, err
}
// 把请求交给后续的Handler处理
resp, err = handler(ctx, req)
return resp, err
}
func (a *grpcAuther) GetClientCredentialsFromMeta(md metadata.MD) (
clientId, clientSecret string) {
cids := md.Get(ClientHeaderKey)
sids := md.Get(ClientSecretKey)
if len(cids) > 0 {
clientId = cids[0]
}
if len(sids) > 0 {
clientSecret = sids[0]
}
return
}
func (a *grpcAuther) validateServiceCredential(clientId, clientSecret string) error {
if clientId == "" && clientSecret == "" {
return status.Errorf(codes.Unauthenticated, "client_id or client_secret is \"\"")
}
if !(clientId == "admin" && clientSecret == "123456") {
return status.Errorf(codes.Unauthenticated, "client_id or client_secret invalidate")
}
return nil
}

View File

@ -12,7 +12,8 @@ import (
func main() { func main() {
// 首先是通过grpc.NewServer()构造一个gRPC服务对象 // 首先是通过grpc.NewServer()构造一个gRPC服务对象
grpcServer := grpc.NewServer() // 补充了全局的认证中间件
grpcServer := grpc.NewServer(grpc.ChainUnaryInterceptor(hello_service.GrpcAuthUnaryServerInterceptor()))
// SDK 提供 服务实现对象的注册 // SDK 提供 服务实现对象的注册
hello_service.RegisterHelloServiceServer(grpcServer, &HelloService{}) hello_service.RegisterHelloServiceServer(grpcServer, &HelloService{})

View File

@ -14,7 +14,12 @@ import (
func main() { func main() {
// grpc.Dial负责和gRPC服务建立链接 // grpc.Dial负责和gRPC服务建立链接
conn, err := grpc.NewClient("localhost:1234", grpc.WithTransportCredentials(insecure.NewCredentials())) conn, err := grpc.NewClient("localhost:1234",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithPerRPCCredentials(hello_service.NewClientAuthentication(
"admin",
"123456",
)))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }