「连载八」对 RPC 方法做自定义认证

    1. 基于 CA 的 TLS 证书认证

    而在实际需求中,常常会对某些模块的 RPC 方法做特殊认证或校验。今天将会讲解、实现这块的功能点

    课前知识

    在 gRPC 中默认定义了 PerRPCCredentials,它就是本章节的主角,是 gRPC 默认提供用于自定义认证的接口,它的作用是将所需的安全认证信息添加到每个 RPC 方法的上下文中。其包含 2 个方法:

    • GetRequestMetadata:获取当前请求认证所需的元数据(metadata)
    • RequireTransportSecurity:是否需要基于 TLS 认证进行安全传输
    1. ├── client
    2. ├── simple_client
    3. ├── simple_http_client
    4. ├── simple_token_client
    5. └── stream_client
    6. ├── conf
    7. ├── pkg
    8. ├── proto
    9. ├── server
    10. ├── simple_http_server
    11. ├── simple_server
    12. ├── simple_token_server
    13. └── stream_server
    14. └── vendor

    gRPC

    在 Client 端,重点实现 type PerRPCCredentials interface 所需的方法,关注两点即可:

    • struct Auth:GetRequestMetadata、RequireTransportSecurity
    • grpc.WithPerRPCCredentials
    1. package main
    2. import (
    3. "context"
    4. "log"
    5. "net"
    6. "google.golang.org/grpc"
    7. "google.golang.org/grpc/codes"
    8. "google.golang.org/grpc/metadata"
    9. "google.golang.org/grpc/status"
    10. "github.com/EDDYCJY/go-grpc-example/pkg/gtls"
    11. pb "github.com/EDDYCJY/go-grpc-example/proto"
    12. type SearchService struct {
    13. auth *Auth
    14. }
    15. func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {
    16. if err := s.auth.Check(ctx); err != nil {
    17. return nil, err
    18. }
    19. return &pb.SearchResponse{Response: r.GetRequest() + " Token Server"}, nil
    20. }
    21. const PORT = "9004"
    22. func main() {
    23. ...
    24. }
    25. type Auth struct {
    26. appKey string
    27. appSecret string
    28. func (a *Auth) Check(ctx context.Context) error {
    29. md, ok := metadata.FromIncomingContext(ctx)
    30. if !ok {
    31. return status.Errorf(codes.Unauthenticated, "自定义认证 Token 失败")
    32. }
    33. appKey string
    34. appSecret string
    35. )
    36. if value, ok := md["app_key"]; ok {
    37. appKey = value[0]
    38. }
    39. if value, ok := md["app_secret"]; ok {
    40. appSecret = value[0]
    41. }
    42. if appKey != a.GetAppKey() || appSecret != a.GetAppSecret() {
    43. return status.Errorf(codes.Unauthenticated, "自定义认证 Token 无效")
    44. }
    45. return nil
    46. }
    47. func (a *Auth) GetAppKey() string {
    48. return "eddycjy"
    49. }
    50. func (a *Auth) GetAppSecret() string {
    51. return "20181005"
    52. }

    在 Server 端就更简单了,实际就是调用 metadata.FromIncomingContext 从上下文中获取 metadata,再在不同的 RPC 方法中进行认证检查

    修改 client.go 的值,制造两者不一致,得到无效结果:

    1. $ go run client.go
    2. 2018/10/05 21:00:05 client.Search err: rpc error: code = Unauthenticated desc = invalid token

    我相信你肯定会问一个个加,也太麻烦了吧?有这个想法的你,应当把 type PerRPCCredentials interface 做成一个拦截器(interceptor)

    参考