【grpc】 8.身份认证
- 在 gRPC 中,可以使用多种不同的认证方式来实现安全通信。以下是常用的认证方式:
- TLS 证书认证:使用 TLS 证书验证服务器和客户端身份。TLS 证书认证是最常见的 gRPC 认证方式,它提供了强大的安全性和可靠性。这种方式需要服务端和客户端都拥有自己的 TLS 证书,并且使用这些证书进行身份验证。
- Token 认证:使用 Token 验证客户端身份。Token 认证是比较简单的一种认证方式,可以用于快速验证客户端身份,但安全性不如 TLS 证书认证。
- OAuth2 认证:使用 OAuth2 协议进行身份验证。OAuth2 认证是一种比较灵活的认证方式,可以使用多种不同的 OAuth2 提供者进行身份验证。
# 官方实现
https://pkg.go.dev/google.golang.org/grpc/credentials?utm_source=godoc
包名:google.golang.org/grpc/credentials
- 默认实现的认证方式
- alts:是由 Google 开发的一种应用层传输安全协议,可以用于保护 gRPC 客户端和服务器之间的通信。ALTS 支持云环境中的认证和授权,并提供了类似于 TLS 的功能,如握手、证书验证和加密
- google认证
- oauth:一种开放标准,用于授权第三方应用程序访问资源。可以通过 OAuth 进行 gRPC 客户端和服务器之间的身份验证和授权,使用 OAuth2 协议时,需要先创建一个 OAuth2 客户端应用程序,并获取相应的客户端 ID 和客户端密钥。
- sts:是一种由 AWS 提供的云服务,可以用于生成、验证和管理安全令牌。可以通过 STS 进行 gRPC 客户端和服务器之间的身份验证和授权,使用 STS 需要在 AWS 上创建一个 IAM 用户,并获取相应的 Access Key ID 和 Secret Access Key。
- tls
- cxds: 由 Istio 开发的一种服务网格认证机制,可以用于保护 gRPC 服务。CxDS 使用证书对客户端和服务器进行身份验证,并使用 mTLS(mutual TLS)来加密通信。
- 自定义
google.golang.org/grpc/credentials
包提供了一组接口和函数,用于实现 gRPC 中的安全传输机制和身份验证。通过使用这些接口和函数,我们可以轻松地实现不同类型的认证方式,以提高 gRPC 的安全性和可靠性。常用的认证方式包括 TLS 证书认证、Token 认证和 OAuth2 认证。
- 默认实现的认证方式
常用的接口和函数:
credentials.TransportCredentials
:定义了安全传输机制的接口。可以使用该接口来创建一个安全传输机制,如 TLS 证书传输机制。credentials.PerRPCCredentials
:定义了客户端身份验证的接口。可以使用该接口来实现 Token 认证或 OAuth2 认证。credentials.NewClientTLSFromFile
:用于创建一个基于证书的安全传输机制。该函数需要传入一个服务器的证书文件和一个 CA 证书文件。credentials.NewServerTLSFromFile
:用于创建一个基于证书的安全传输机制。该函数需要传入一个服务器的证书文件和一个服务器的私钥文件。
# 1. token认证示例
客户端(或者使用metadata直接传输
MyTokenCredentials
自定义凭证,实现了credentials.PerRPCCredentials
接口中的两个方法。GetRequestMetadata
方法返回一个包含认证信息的 map,RequireTransportSecurity
方法返回一个布尔值,表示是否要求使用安全传输。使用grpc.Dial
函数创建了一个 gRPC 连接,并将MyTokenCredentials
作为参数传入grpc.WithPerRPCCredentials
函数,以实现 Token 认证。
package main import ( helloproto "bigox-rpc/proto" "context" "fmt" "google.golang.org/grpc" ) // 创建一个基于 Token 的自定义凭证 type MyTokenCredentials struct { Token string } func (c *MyTokenCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { return map[string]string{ "token": c.Token, }, nil } func (c *MyTokenCredentials) RequireTransportSecurity() bool { return false } func main() { conn, err := grpc.Dial( "127.0.0.1:8080", grpc.WithPerRPCCredentials(&MyTokenCredentials{Token: "token123"}), grpc.WithInsecure(), ) if err != nil { panic(err) } defer conn.Close() c := helloproto.NewGreeterClient(conn) res, err := c.Hello(context.Background(), &helloproto.Req{Msg: "liushahe"}) if err != nil { panic(err) } fmt.Println(res.Msg) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43server
grpc.ChainUnaryInterceptor(func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, fmt.Errorf("missing metadata") } if len(md["token"]) == 0 || md["token"][0] != "my-secret-token" { return nil, fmt.Errorf("invalid token") } // todo res, err := handler(ctx, req) return res, err }),
1
2
3
4
5
6
7
8
9
10
11
12
13
# 2. TLS 证书认证
TLS 证书认证是一种基于证书的认证方式,用于保护 gRPC 通信的安全性。
在 TLS 证书认证中,gRPC 客户端和服务器之间的通信通过 TLS 连接加密和验证。gRPC 服务器使用证书来证明其身份,而 gRPC 客户端使用证书来验证服务器的身份。通过 TLS 证书认证,可以有效地保护 gRPC 通信的安全性和可靠性。
server
package main import ( "context" "log" "net" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) type HelloServiceImpl struct{} func (s *HelloServiceImpl) SayHello(ctx context.Context, request *HelloRequest) (*HelloResponse, error) { return &HelloResponse{Message: "Hello " + request.Name}, nil } func main() { // 加载证书 creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key") if err != nil { log.Fatalf("failed to load credentials: %v", err) } // 监听端口 listener, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("failed to listen: %v", err) } // 创建 gRPC 服务器,启用 TLS 证书认证 server := grpc.NewServer(grpc.Creds(creds)) RegisterHelloServiceServer(server, &HelloServiceImpl{}) log.Println("server is running...") if err := server.Serve(listener); err != nil { log.Fatalf("failed to serve: %v", err) } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40client.go
package main import ( "context" "fmt" "log" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { // 加载证书 creds, err := credentials.NewClientTLSFromFile("server.crt", "") if err != nil { log.Fatalf("failed to load credentials: %v", err) } // 创建 gRPC 连接 conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds)) if err != nil { log.Fatalf("failed to dial: %v", err) } defer conn.Close() // 创建客户端 client := NewHelloServiceClient(conn) // 发送请求 request := &HelloRequest{Name: "world"} response, err := client.SayHello(context.Background(), request) if err != nil { log.Fatalf("failed to call SayHello: %v", err) } fmt.Println(response.Message) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 3. Oauth认证
# 3.1 oauth认证
server端只需要校验token即可
client
import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/oauth" ) func main() { // 使用 OAuth Token 认证 perRPC := oauth.TokenSource{ TokenSource: myTokenSource, // 自定义的 TokenSource } creds := credentials.NewTLS(&tls.Config{}) opts := []grpc.DialOption{ grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(perRPC), } // 建立连接 conn, err := grpc.Dial("localhost:50051", opts...) if err != nil { fmt.Printf("Failed to connect: %v", err) return } defer conn.Close() // TODO: 调用 gRPC 服务的代码 } // 自定义 TokenSource,用于获取 OAuth Token func myTokenSource() (string, error) { // TODO: 实现获取 OAuth Token 的逻辑 return "mytoken", nil }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 3.2 Oauth2 认证
client
package main import ( helloproto "bigox-rpc/proto" "context" "fmt" "golang.org/x/oauth2" "google.golang.org/grpc" "google.golang.org/grpc/credentials/oauth" "time" ) func main() { // 创建 OAuth2 客户端应用程序 oauthConfig := &oauth2.Config{ ClientID: "my_client_id", ClientSecret: "my_client_secret", RedirectURL: "http://localhost:8080/callback", Scopes: []string{"my_scope"}, Endpoint: oauth2.Endpoint{ AuthURL: "https://oauth.example.com/authorize", TokenURL: "https://oauth.example.com/token", }, } // 获取 OAuth2.0 Token token, err := getToken(oauthConfig) if err != nil { fmt.Printf("Failed to get token: %v", err) return } // 使用 OAuth2.0 Token 认证 perRPC := oauth.TokenSource{ TokenSource: oauthConfig.TokenSource(context.Background(), token), } conn, err := grpc.Dial( "127.0.0.1:8080", grpc.WithPerRPCCredentials(perRPC), grpc.WithInsecure(), ) if err != nil { panic(err) } defer conn.Close() c := helloproto.NewGreeterClient(conn) res, err := c.Hello(context.Background(), &helloproto.Req{Msg: "liushahe"}) if err != nil { panic(err) } fmt.Println(res.Msg) } // 获取 OAuth2.0 Token func getToken(config *oauth2.Config) (*oauth2.Token, error) { // TODO: 实现获取 OAuth2.0 Token 的逻辑 return &oauth2.Token{ AccessToken: "my_token", RefreshToken: "my_refresh_token", Expiry: time.Now().Add(time.Hour), }, nil }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
上次更新: 2023/05/04, 15:30:48