0%

RPC序列(三)-gRPC安全认证

未认证的RPC服务可能导致服务被攻击、注入恶意数据、数据泄露等安全问题。gRPC框架自带的两种安全认证方式,这里简单介绍这两种认证方式,并用Golang进行简单实现。

认证

gRPC内置两种认证方式:

  • SSL/TLS认证方式
  • 自定义认证方式

SSL/TLS认证

什么是SSL/TLS呢?SSL是安全套接字层(secure sockets layer),TLS是传输层安全 (transport layer security),就是在应用层和TCP层之间新加一层加密,这样可以保证信息传输的安全性。

制作TLS证书

通过OpenSSL工具生成私钥,加密方式RSA,密码大小2048位。

1
$ openssl genrsa -out server.key 2048

使用私钥生成自签名的证书文件

1
openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650

填入证书自定义信息,注意server name 填写的内容,后面会用到。

示例项目

First: server添加TLS认证

通过密钥和刚刚制作的证书生成服务端传输证书。部分代码如下:

1
2
3
4
5
6
7
// TLS认证
creds, err := credentials.NewServerTLSFromFile("./keys/server.pem", "./keys/server.key" /*制作证书存放路径*/)
if err != nil {
fmt.Printf("failed to generate credentials err:%v\n", err)
return
}
server := grpc.NewServer(grpc.Creds(creds))
Second: client添加TLS认证

通过制作的证书生成客户端传输证书。部分代码如下:

1
2
3
4
5
6
7
// TLS认证
creds, err := credentials.NewClientTLSFromFile("./keys/server.pem", "server name" /*制作证书时的server name*/)
if err != nil {
fmt.Printf("failed to generate credentials err:%v\n", err)
return
}
conn, err := grpc.Dial("localhost:9000", grpc.WithTransportCredentials(creds))

到这里gRPC的TLS单向认证的简单实现就完成了。

自定义Token认证

通过自定义的身份认证方式进行认证客户端是否有权限。简单理解就是处理请求前对请求的内容进行身份认证。

示例项目

First: server添加鉴权拦截器

新增鉴权拦截器,示例代码如下:

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
func Interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
err := Auth(ctx)
if err != nil {
return nil, err
}
// 继续处理请求
return handler(ctx, req)
}


func Auth(ctx context.Context) error {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return errors.New("无Token认证信息")
}

// authentication
val := md.Get("app_id")
if len(val) < 0 {
return errors.New("app_id is empty")
}
appID := val[0]

val = md.Get("secret_key")
if len(val) < 0 {
return errors.New("secret_key is empty")
}
appKey := val[0]

// 简单模拟鉴权方式
if _, ok := tokenMap[appID]; !ok || appKey != tokenMap[appID] {
return errors.New("unauthorized")
}

return nil
}

将鉴权拦截器加入服务选项,代码如下:

1
2
3
4
5
// token auth
opts = append(opts, grpc.UnaryInterceptor(interceptor.Interceptor))

// rpc server
server := grpc.NewServer(opts...)
second: client添加身份认证请求参数
方式一:实现PerRPCCredentials接口

该接口有两个方法

  1. GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) 返回请求的身份认证数据。
  2. RequireTransportSecurity() bool 返回是否开启TLS认证
1
2
3
4
5
6
7
8
9
type authCredential struct{}

func (*authCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{"app_id":"123456","secret_key":"key"}, nil
}

func (*authCredential) RequireTransportSecurity() bool {
return true
}

然后将该自定义认证加入到客户端选项中:

1
2
3
// 自定义认证方式
opts = append(opts, grpc.WithPerRPCCredentials(new(authCredential)))

方式二:context添加元数据

请求方法的context添加身份认证元数据。

1
2
3
4
5
6
// 认证参数
md := metadata.Pairs("app_id", "123456", "secret_key", "secret_key")
ctx := metadata.NewOutgoingContext(context.Background(), md)

// todo
// resp := client.Function(ctx, reqMsg...)

到这自定义身份认证的简单实现就完成了。

项目源码

1
git clone https://github.com/zhouxuwen/grpcdemo-go.git