未认证的RPC服务可能导致服务被攻击、注入恶意数据、数据泄露等安全问题。gRPC框架自带的两种安全认证方式,这里简单介绍这两种认证方式,并用Golang进行简单实现。
认证
gRPC内置两种认证方式:
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
| 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
| creds, err := credentials.NewClientTLSFromFile("./keys/server.pem", "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认证信息") }
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
| opts = append(opts, grpc.UnaryInterceptor(interceptor.Interceptor))
server := grpc.NewServer(opts...)
|
second: client添加身份认证请求参数
方式一:实现PerRPCCredentials接口
该接口有两个方法
GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
返回请求的身份认证数据。
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)
|
到这自定义身份认证的简单实现就完成了。
项目源码
1
| git clone https://github.com/zhouxuwen/grpcdemo-go.git
|