创建时间: 2019/9/6 13:54
作者: sunpengwei1992@aliyun.com

gRPC安全认证介绍

  • gRPC被设计成可以利用插件的形式支持多种授权认证机制,你可以采用自己喜欢的,简单的,认为方便的一种方式,选择权在用户手里
  • 支持的授权认证机制如下
    1. SSL/TLS认证
    2. 自定义Token认证
  • SSL/TLS的概念可以参考下面的文章
  • https://www.techug.com/post/https-ssl-tls.html

    SSL/TLS认证方式

  • 首先通过openssl生成证书和私钥,命令如下

    1. //生成私钥
    2. openssl genrsa -out server.key 2048
    3. //生成证书
    4. openssl req -new -x509 -sha256 -key server.key -out server.crt -days 36500
    5. //按照提示输入如下信息
    6. 国家名称
    7. Country Name (2 letter code) [AU]:
    8. //省名称
    9. State or Province Name (full name) [Some-State]:
    10. //城市名称
    11. Locality Name (eg, city) []:
    12. //理解为公司名称
    13. Organization Name (eg, company) [Internet Widgits Pty Ltd]:
    14. //理解为你所在部门的名称
    15. Organizational Unit Name (eg, section) []:
    16. //你的服务器名称(网站名称)
    17. Common Name (e.g. server FQDN or YOUR name) []:
    18. //联系邮箱
    19. Email Address []:
  • 服务端如下

    1. func StartServer() {
    2. lis, err := net.Listen("tcp", "127.0.0.1:8090")
    3. if err != nil {
    4. log.Fatalf("failed to listen: %v", err)
    5. }
    6. // TLS认证
    7. // 两个入参分别是 (certFile, keyFile string)
    8. // 自签名证书文件和私钥文件
    9. creds, err := credentials.NewServerTLSFromFile("cert", "key")
    10. //创建grpcServer传入证书
    11. gRpcServer := grpc.NewServer(grpc.Creds(creds))
    12. pb.RegisterHelloServiceServer(gRpcServer, &HelloServiceServer{})
    13. gRpcServer.Serve(lis)
    14. }
  • 客户端如下

    1. func StartClient() {
    2. // TLS认证
    3. creds, err := credentials.NewClientTLSFromFile("cert", "ServerName")
    4. //连接服务器
    5. conn, err := grpc.Dial("127.0.0.1:8090",grpc.WithTransportCredentials(creds) )
    6. if err != nil{
    7. fmt.Println(err)
    8. return
    9. }
    10. c := pb.NewHelloServiceClient(conn)
    11. //
    12. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    13. defer cancel()
    14. r, err := c.HelloWorldClientAndServerStream(ctx, grpc.EmptyCallOption{})
    15. if err != nil {
    16. log.Fatalf("%v", err)
    17. return
    18. }
    19. r.Send(&pb.HelloRequest{Request: "my is golang gRpc client "})
    20. r.CloseSend()
    21. }

    如上就开启了gRPC的TLS认证

    Token认证

  • 我们先看一个gRPC提供我们的一个接口,这个接口终有两个方法,接口位于credentials
    包下,这个接口需要客户端来实现

  • 第一个方法作用是获取元数据信息,也就是客户端提供的key,value对,context用于控制超时和取消,uri是请求入口处的uri
  • 第二个方法的作用是否需要基于 TLS 认证进行安全传输是,如果返回值是true,则必须加上TLS验证,返回值是false则不用

    1. type PerRPCCredentials interface {
    2. GetRequestMetadata(ctx context.Context, uri ...string)
    3. (map[string]string, error)
    4. RequireTransportSecurity() bool
    5. }
  • 客户端端实现接口,代码如下

    1. //自定义token认证
    2. type CustomerTokenAuth struct {
    3. }
    4. //获取元数据
    5. func (c CustomerTokenAuth) GetRequestMetadata(ctx context.Context,
    6. uri...string) (map[string]string, error) {
    7. return map[string]string{
    8. "appId": "master",
    9. "appkey": "1aqfs5g456j",
    10. }, nil
    11. }
    12. //是否开启传输安全 TLS
    13. func (c CustomerTokenAuth) RequireTransportSecurity() bool {
    14. return false
    15. }
  • 客户端按照如下方式使用

    1. var opts []grpc.DialOption
    2. //grpc.WithInsecure()这个是一定要添加的,代表开启安全的选项
    3. opts =append(opts,grpc.WithInsecure())
    4. //添加自定义token验证
    5. opts = append(opts,grpc.WithPerRPCCredentials(new(CustomerTokenAuth)))
    6. //连接服务端
    7. conn, err := grpc.Dial("127.0.0.1:8090",opts...)
  • 服务端按照如下方式校验,当然我们也可以使用拦截器的形式对每个方法进行拦截,而不是像如下在每个方法中硬编码是的。

    1. type HelloServiceServer struct {
    2. }
    3. //这是服务端实现的一个方法
    4. func (*HelloServiceServer2) HelloWorld(ctx context.Context,
    5. req *pb.HelloRequest) (*pb.HelloResponse, error) {
    6. //获取元数据信息
    7. md,ok := metadata.FromIncomingContext(ctx)
    8. if !ok {
    9. return nil,errors.New("未传输token")
    10. }
    11. var (
    12. appId string
    13. appKey string
    14. )
    15. if val, ok := md["appId"]; ok {
    16. appid = val[0]
    17. }
    18. if val, ok := md["appKey"]; ok {
    19. appkey = val[0]
    20. }
    21. //进行校验的信息是否正确
    22. if appid != "123" || appkey != "456" {
    23. return nil, errors.New("token传输不正确")
    24. }
    25. return &pb.HelloResponse{Response: "hello my is gRpcServer"}, nil
    26. }

    总结

    gRPC将各种认证方式浓缩统一到一个凭证(credentials)上,可以单独使用一种凭证,比如只使用TLS凭证或者只使用自定义凭证,也可以多种凭证组合,gRPC提供统一的API验证机制,使研发人员使用方便,这也是gRPC设计的巧妙之处

欢迎大家关注微信公众号:“golang那点事”,更多精彩期待你的到来
GoLang公众号.jpg