扫码订阅《 Go语言微服务框架学习实践》或入驻星球,即可阅读文章!

GOLANG ROADMAP · 知识星球

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go路线图
  • 👶 初级要求:负责一个模块

    • 📗 薪资·10-15k 技能梳理
  • 🧑 中高级要求:负责一个方向

    • 📘 薪资·15-25k 技能梳理
  • 🧔 资深要求:负责一个领域

    • 📒 薪资·25-40k 技能梳理
  • 🧙 专家要求:负责多个领域

    • 📕 薪资·40k以上 技能梳理
Go学院
  • Go小课
  • Go小考
  • Go宝典
Go资源
  • 推荐资源

    • 优质课程
    • 推荐图书
    • 开源项目
  • 资源下载

    • 视频资源
    • 文档资源
    • 帮找资源
Go求职
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
  • 求职服务

    • 内推互助
    • 求职助力
    • 内推公司
推广返佣
  • 返佣排行
  • 返佣规则
  • 推广学院
更多
  • 用户中心

    • 我的信息
    • 我的消息
    • 我的返佣
author-avatar

GOLANG ROADMAP


首页
Go路线图
  • 👶 初级要求:负责一个模块

    • 📗 薪资·10-15k 技能梳理
  • 🧑 中高级要求:负责一个方向

    • 📘 薪资·15-25k 技能梳理
  • 🧔 资深要求:负责一个领域

    • 📒 薪资·25-40k 技能梳理
  • 🧙 专家要求:负责多个领域

    • 📕 薪资·40k以上 技能梳理
Go学院
  • Go小课
  • Go小考
  • Go宝典
Go资源
  • 推荐资源

    • 优质课程
    • 推荐图书
    • 开源项目
  • 资源下载

    • 视频资源
    • 文档资源
    • 帮找资源
Go求职
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
  • 求职服务

    • 内推互助
    • 求职助力
    • 内推公司
推广返佣
  • 返佣排行
  • 返佣规则
  • 推广学院
更多
  • 用户中心

    • 我的信息
    • 我的消息
    • 我的返佣
  • 《Go语言微服务框架学习实践》

    • 课程介绍
  • RPC远程调用机制

  • gRPC微服务框架

    • 第1节:gRPC介绍和安装
    • 第2节:gRPC框架使用
    • 第3节:gRPC调用
    • 第4节:TLS验证和Token认证
    • 第5节:拦截器的使用
  • go-micro微服务框架

扫码订阅《 Go语言微服务框架学习实践》或入驻星球,即可阅读文章!

第4节:TLS验证和Token认证


GOLANG ROADMAP

上节课我们学习掌握了grpc-go框架的四种流模式。在实际的生产环境中,一个功能完整的服务,不仅包含基本的方法调用和数据交互的功能,还包括授权认证,数据追踪,负载均衡等方面。本节课,我们来看一下除了在gRPC调用过程中,如何实现授权认证,以及如何进行拦截处理。

# 一 授权认证

gRPC中默认支持两种授权方式,分别是:SSL/TLS认证方式、基于Token的认证方式。

# 1.1 SSL/TLS认证方式

SSL全称是Secure Sockets Layer,又被称之为安全套接字层,是一种标准安全协议,用于在通信过程中建立客户端与服务器之间的加密链接。 TLS的全称是Transport Layer Security,TLS是SSL的升级版。在使用的过程中,往往习惯于将SSL和TLS组合在一起写作SSL/TLS。 简而言之,SSL/TLS是一种用于网络通信中加密的安全协议。

1.1.1 SSL/TLS工作原理

使用SSL/TLS协议对通信连接进行安全加密,是通过非对称加密的方式来实现的。所谓非对称加密方式又称之为公钥加密,密钥对由公钥和私钥两种密钥组成。私钥和公钥成对存在,先生成私钥,通过私钥生成对应的公钥。公钥可以公开,私钥进行妥善保存。

在加密过程中:客户端想要向服务器发起链接,首先会先向服务端请求要加密的公钥。获取到公钥后客户端使用公钥将信息进行加密,服务端接收到加密信息,使用私钥对信息进行解密并进行其他后续处理,完成整个信道加密并实现数据传输的过程。

1.1.2 制作证书

可以自己在本机计算机上安装openssl,并生成相应的证书。

openssl ecparam -genkey -name secp384r1 -out server.key
openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
1
2

1.1.3 编程实现服务端

type MathManager struct {
}

func (mm *MathManager) AddMethod(ctx context.Context, request *message.RequestArgs) (response *message.Response, err error) {
    fmt.Println(" 服务端 Add方法 ")
    result := request.Args1 + request.Args2
    fmt.Println(" 计算结果是:", result)
    response = new(message.Response)
    response.Code = 1;
    response.Message = "执行成功"
    return response, nil
}

func main() {

    //TLS认证
    creds, err := credentials.NewServerTLSFromFile("./keys/server.pem", "./keys/server.key")
    if err != nil {
        grpclog.Fatal("加载在证书文件失败", err)
    }

    //实例化grpc server, 开启TLS认证
    server := grpc.NewServer(grpc.Creds(creds))

    message.RegisterMathServiceServer(server, new(MathManager))

    lis, err := net.Listen("tcp", ":8092")
    if err != nil {
        panic(err.Error())
    }
    server.Serve(lis)
}
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

1.1.3 编程实现客户端

func main() {

    //TLS连接
    creds, err := credentials.NewClientTLSFromFile("./keys/server.pem", "go-grpc-example")
    if err != nil {
        panic(err.Error())
    }
    //1、Dail连接
    conn, err := grpc.Dial("localhost:8092", grpc.WithTransportCredentials(creds))
    if err != nil {
        panic(err.Error())
    }
    defer conn.Close()

    serviceClient := message.NewMathServiceClient(conn)

    addArgs := message.RequestArgs{Args1: 3, Args2: 5}

    response, err := serviceClient.AddMethod(context.Background(), &addArgs)
    if err != nil {
        grpclog.Fatal(err.Error())
    }

    fmt.Println(response.GetCode(), response.GetMessage())
}
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

# 1.2 基于Token认证方式

1.2.1 Token认证介绍

在web应用的开发过程中,我们往往还会使用另外一种认证方式进行身份验证,那就是:Token认证。基于Token的身份验证是无状态,不需要将用户信息服务存在服务器或者session中。

1.2.2 Token认证过程

基于Token认证的身份验证主要过程是:客户端在发送请求前,首先向服务器发起请求,服务器返回一个生成的token给客户端。客户端将token保存下来,用于后续每次请求时,携带着token参数。服务端在进行处理请求之前,会首先对token进行验证,只有token验证成功了,才会处理并返回相关的数据。

1.2.3 gRPC的自定义Token认证

在gRPC中,允许开发者自定义自己的认证规则,通过

grpc.WithPerRPCCredentials()
1

设置自定义的认证规则。WithPerRPCCredentials方法接收一个PerRPCCredentials类型的参数,进一步查看可以知道PerRPCCredentials是一个接口,定义如下:

type PerRPCCredentials interface {
    GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
    RequireTransportSecurity() bool
}
1
2
3
4

因此,开发者可以实现以上接口,来定义自己的token信息。

在本案例中,我们自定义的token认证结构体如下:

//token认证
type TokenAuthentication struct {
    AppKey    string
    AppSecret string
}

//组织token信息
func (ta *TokenAuthentication) RequestMetaData(ctx context.Context, uri ...string) (map[string]string, error) {
    return map[string]string{
        "appid":    ta.AppKey,
        "appkey": ta.AppSecret,
    }, nil
}

//是否基于TLS认证进行安全传输
func (a *TokenAuthentication) RequireTransportSecurity() bool {
    return true
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

需要注意的是,RequestMetaData方法中的appid和appkey字段均需要保持小写,不能大写。RequireTransportSecurity方法用于设置是否基于tls认证进行安全传输。

在客户端进行连接时,我们将自定义的token认证信息作为参数进行传入。

auth := TokenAuthentication{
        AppKey:    "hello",
        AppSecret: "20190812",
}
conn, err := grpc.Dial("localhost:8093", grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(&auth))
if err != nil {
    panic(err.Error())
}
1
2
3
4
5
6
7
8

1.2.4 服务端

在服务端的调用方法中实现对token请求参数的判断,可以通过metadata获取token认证信息。具体实现如下:

func (mm *MathManager) AddMethod(ctx context.Context, request *message.RequestArgs) (response *message.Response, err error) {

    //通过metadata
    md, exist := metadata.FromIncomingContext(ctx)
    if !exist {
        return nil, status.Errorf(codes.Unauthenticated, "无Token认证信息")
    }

    var appKey string
    var appSecret string

    if key, ok := md["appid"]; ok {
        appKey = key[0]
    }

    if secret, ok := md["appkey"]; ok {
        appSecret = secret[0]
    }

    if appKey != "hello" || appSecret != "20190812" {
        return nil, status.Errorf(codes.Unauthenticated, "Token 不合法")
    }
    fmt.Println(" 服务端 Add方法 ")
    result := request.Args1 + request.Args2
    fmt.Println(" 计算结果是:", result)
    response = new(message.Response)
    response.Code = 1;
    response.Message = "执行成功"
    return response, 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

最后,运行项目,token认证成功。客户端修改token信息,再次运行,会发现提示token不合法。

  • 一 授权认证
  • 1.1 SSL/TLS认证方式
  • 1.2 基于Token认证方式