Golang——gRPC认证

一. OpenSSL

        1.1 介绍

        OpenSSL是一个开放源代码的软件库包,用于支持网络通讯过程中的加密。这个库提供的功能包含了SSL和TLS协议的实现,并可用于生成密钥、证书、进行密码运算等。

        其组成主要包括一下三个组件:

  1. openssl:多用途的命令行工具

  2. libcrypto:加密算法库

  3. libssl:加密模块应用库,实现了ssl及tls

openssl可以实现秘钥证书管理、对称加密和非对称加密 。

        官网:[ Downloads ] - /source/index.html

        1.2 Windows安装方法

        OpenSSL官网没有提供windows版本的安装包,可以选择其它开源平台提供的工具。

Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

        以该工具为例:

        进入下载界面,选择下载的版本,下载完,之后安装即可。

        1.3 生成公钥和私钥

        openssl命令详解-CSDN博客

生成私钥:openssl genrsa -out rsa_private_key.pem 1024
生成公钥:openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

二. gRPC认证

        gRPC默认内置了两种认证方式:

  • SSL/TLS认证
  • 基于Token的认证

        同时,gRPC提供了接口用于扩展自定义认证方式。

        2.1 TLS认证

        2.1.1 什么是TLS认证

        TLS(Transport Layer Security,安全传输层),TLS是建立在传输层TCP协议之上的协议,服务于应用层,它的前身是SSL(Secure Socket Layer,安全套接字层),它实现了将应用层的报文进行加密后再交由TCP进行传输的功能。

        2.1.2 TLS的作用

TLS协议主要解决如下三个网络安全问题。

  • 保密(message privacy),保密通过加密encryption实现,所有信息都加密传输,第三方无法嗅探;
  • 完整性(message integrity),通过MAC校验机制,一旦被篡改,通信双方会立刻发现;
  • 认证(mutual authentication),双方认证,双方都可以配备证书,防止身份被冒充;

        2.1.3 TLS认证实例

  • 证书制作

        制作公钥:自签名公钥(x509),   制作私钥。

#生成一个名为server_private.key的RSA私钥,使用SHA256算法和4096位密钥长度。然后使用该私钥生成一个有效期为36500天的自签名证书,并将其保存为名为server.pem的文件。同时在证书中添加subjectAltName扩展,指定DNS名称为www.wy.com。
openssl req -newkey rsa:4096 -nodes -sha256 -keyout server_private.key -x509 -days 36500 -out server.pem -addext "subjectAltName =DNS:www.wy.com"
  •  openssl req:生成自签名证书
  • -newkey rsa:4096 :生成新的4096位rsa密钥对
  • -sha256:使用sha256加密
  • -keyout:指定生成的私钥文件
  • -x509:指输出证书
  • -days 36500:有效期 36500
  • -out:输出证书的文件名
  • -addext:添加扩展

        注意需要在证书中添加subjectAltName扩展,指定DNS名称。不然在客户端连接服务器时会报错,报错信息为:

        rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead"

        因为go1.15 版本开始废弃CommonName,因此推荐使用SAN证书。如果想兼容之前的方式,需要设置环境变量 GODEBUG为 x509ignoreCN=0。(创建 SSL/TLS 证书时,证书依赖于传统的 Common Name (CN) 字段,而没有使用现代标准所推荐的 Subject Alternative Names (SANs) 字段。现代的 TLS 客户端(比如最新版本的浏览器和安全工具)要求证书使用 SANs 字段来指定有效的主机名。)

        自定义信息: 

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN #国家
State or Province Name (full name) [Some-State]:SHANGHAI #省份
Locality Name (eg, city) []:SHANGHAI #城市
Organization Name (eg, company) [Internet Widgits Pty Ltd]:BF #公司
Organizational Unit Name (eg, section) []:Dev #部门
Common Name (e.g. server FQDN or YOUR name) []:www.wy.com #服务器名称
Email Address []:xxx@xxx.com #邮箱地址
  • 目录结构

  • 示例代码 

         服务端代码:

package main

import (
	"context"
	"fmt"
	"net"
	hello "sample-app/grpc/proto"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials" //引入gRPC认证包
)

const (
    //服务器地址
    Addr = "127.0.0.1:8080"
)

type helloService struct{}

//定义hello 服务
var HelloService = helloService{}

//实现proto hello service方法
func (h helloService) SayHello(c context.Context, req *hello.HelloRequest) (*hello.HelloResponse, error) {
	resp := new(hello.HelloResponse)
	resp.Message = fmt.Sprintf("Hello %s", req.Name)
	return resp, nil
}

func main() {
	ls, err := net.Listen("tcp", Addr)
	if err != nil {
		fmt.Println(err)
		return
	}
	//TLS认证
	cert, err := credentials.NewServerTLSFromFile("..\\..\\key\\server.pem", "..\\..\\key\\server_private.key")
	if err != nil {
		fmt.Println(err)
		return
	}
	//新建一个grpc服务器,并开启TLS认证
    //上面监听并没有进行连接客户端
	server := grpc.NewServer(grpc.Creds(cert))
	//注册HelloService
	hello.RegisterHelloServer(server, HelloService)
	fmt.Println("Listen on" + Addr + "with TLS")
    //这里面才会连接客户端,需要进行认证
	server.Serve(ls)
}
  • credentials.NewServerTLSFromFile:从输入证书文件和密钥文件为服务端构造TLS凭证

  • grpc.Creds:返回一个ServerOption,用于设置服务器连接的凭证。

        客户端代码:

package main

import (
	"context"
	"fmt"
	hello "sample-app/grpc/proto"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

const (、
    //gRPC服务器地址
	Addr = "127.0.0.1:8080"
)

func main() {
	//TLS连接
	cert, err := credentials.NewClientTLSFromFile("..\\..\\key\\server.pem", "www.wy.com")
	if err != nil {
		fmt.Println("credentials fail ", err)
		return
	}
    //请求连接的时候 需要认证
	conn, err := grpc.Dial(Addr, grpc.WithTransportCredentials(cert))
	if err != nil {
		fmt.Println("Dial fail", err)
		return
	}
	defer conn.Close()

	c := hello.NewHelloClient(conn)
	req := new(hello.HelloRequest)
	req.Name = "gRPC"

	resp, err := c.SayHello(context.Background(), req)
	if err != nil {
		fmt.Println("say hello fail", err)
		return
	}

	fmt.Println(resp.Message)
}
  • credentials.NewClientTLSFromFile:从输入的证书文件中为客户端构造TLS凭证。

  • grpc.WithTransportCredentials:配置连接级别的安全凭证(例如,TLS/SSL),返回一个DialOption,用于连接服务器。

        proto文件:

syntax="proto3";
package hello;
option go_package="hello";

service Hello
{
    rpc SayHello(HelloRequest)returns(HelloResponse){}; 
}

message HelloRequest
{
    string name = 1;
}

message HelloResponse
{
    string message = 1;
}

使用下面命令生成pb.go文件:

protoc --go_out=plugins=grpc:"生成pb.go文件地址" -I="proto文件地址" "proto文件地址\文件" 

演示:

        实际TLS认证不是这样,客户端和服务器时分离的。客户端有证书(包含公钥),服务端有证书和私钥。

        客户端发送请求给服务器请求连接,服务器将证书通过私钥加密后发送给客户端。客户端有证书,里面包含服务器私钥对应的公钥。使用公钥对数据进行解密,获得证书数据,与本地证书数据进行比较。

         2.2 Token认证

        继续扩展上面的代码,实现TLS+Token认证机制。

        2.2.1 什么是Token认证

        Token认证是一种基于Token的身份验证方法,用于在客户端和服务器之间进行身份验证。以下是Token认证的主要概念、流程以及优缺点:

  • 主要概念

    • Token的含义:Token(令牌)是服务端生成的一串字符串,作为客户端进行请求的一个标识。
    • Token的组成:一般包括用户身份标识(uid)、时间戳(time)和签名(sign)等元素。
    • Token的作用:Token主要用于身份验证、授权、会话管理和跨域资源共享(CORS)等方面。
  • 认证流程

    • 用户登录并获取Token:用户使用用户名和密码登录,成功后服务端生成Token并发送给客户端。
    • 客户端存储和使用Token:客户端将Token保存在本地(如cookie或localStorage),并在后续请求中携带该Token。
    • 服务端验证Token:服务端收到请求后,验证Token的合法性,若合法则处理请求并返回数据。

        2.2.2 示例代码

        根据上面的代码,实现TLS+Token认证机制。

  • 认证原理

        客户端发送请求,会将Token放到context.Context上下文中,服务器收到请求,从上下文中获取Token验证,然后进行下一步操作。

  • 目录结构

  • 客户端代码

        grpc/credential包内默认定义了PerRPCCredentials接口,是提供用于自定义接口,他的作用是将所需安全认证信息添加到每个RPC上下文中。其包含两个方法。

type PerRPCCredentials interface {
    //获取当前请求认证所需的元数据
	GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
    //是否需要基于TLS认证进行安全传输
	RequireTransportSecurity() bool
}
package main

import (
	"context"
	"fmt"
	hello "sample-app/grpc/proto"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

const (
	Addr = "127.0.0.1:8080"
	//是否使用TLS
	OpenTLS = true
)

// 自定义认证
type Token struct {
	Appid  string
	Appkey string
}

// 实现自定义认证方法
func (t Token) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"appid":  t.Appid,
		"appkey": t.Appkey,
	}, nil
}

// 实现自定义认证是否开启TLS
func (t Token) RequireTransportSecurity() bool {
	return OpenTLS
}

func main() {
	//TLS连接
	var opt []grpc.DialOption
	if OpenTLS {
		cert, err := credentials.NewClientTLSFromFile("..\\..\\key\\server.pem", "www.wy.com")
		if err != nil {
			fmt.Println("credentials fail ", err)
			return
		}
		opt = append(opt, grpc.WithTransportCredentials(cert))
	} else {
		opt = append(opt, grpc.WithInsecure())
	}
	//使用自定义认证
	tk := Token{
		Appid:  "101010",
		Appkey: "i am a key",
	}
	opt = append(opt, grpc.WithPerRPCCredentials(&tk))

	conn, err := grpc.Dial(Addr, opt...)
	if err != nil {
		fmt.Println("Dial fail", err)
		return
	}
	defer conn.Close()
	//初始化服务器
	c := hello.NewHelloClient(conn)

	req := new(hello.HelloRequest)
	req.Name = "gRPC"

	resp, err := c.SayHello(context.Background(), req)
	if err != nil {
		fmt.Println("say hello fail", err)
		return
	}

	fmt.Println(resp.Message)
}

        定义一个结构Token,包含Token所需属性字段。实现PerRPCCredentials接口的两个方法。每次调用token信息会通过请求metadata传输到服务端。

        下面查看服务端如何获取metadata中信息。 

  • 服务端代码

        使用metadata.FromIncomingContext:从上下文中获取元数据。

package main

import (
	"context"
	"fmt"
	"net"
	hello "sample-app/grpc/proto"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials" //引入gRPC认证包
	"google.golang.org/grpc/metadata"
)

var Addr = "127.0.0.1:8080"

type helloService struct{}

var HelloService = helloService{}

func (h helloService) SayHello(c context.Context, req *hello.HelloRequest) (*hello.HelloResponse, error) {
	//认证
	md, ok := metadata.FromIncomingContext(c)
	if !ok {
		return nil, grpc.Errorf(codes.Unauthenticated, "无Token认证信息")
	}

	var appid string
	var appkey string
	vals := md.Get("appid")
	if len(vals) != 0 {
		appid = vals[0]
	}

	val_key := md.Get("appkey")
	if len(val_key) != 0 {
		appkey = val_key[0]
	}
	//认证token
	if appid != "101010" || appkey != "i am a key" {
		return nil, grpc.Errorf(codes.Unauthenticated, "Token认证信息错误: Appid:%s, Appkey:%s", appid, appkey)
	}
	//fmt.Println("authenticated succ " + appid + "-" + appkey)

	resp := new(hello.HelloResponse)
	resp.Message = fmt.Sprintf("Hello %s \nToken info: Appid=%s, AppKey=%s", req.Name, appid, appkey)

	return resp, nil
}

func main() {
	ls, err := net.Listen("tcp", Addr)
	if err != nil {
		fmt.Println(err)
		return
	}
	//TLS认证
	cert, err := credentials.NewServerTLSFromFile("..\\..\\key\\server.pem", "..\\..\\key\\server_private.key")
	if err != nil {
		fmt.Println(err)
		return
	}
	//新建一个grpc服务器,并开启TLS认证
	server := grpc.NewServer(grpc.Creds(cert))
	//注册HelloService
	hello.RegisterHelloServer(server, HelloService)
	fmt.Println("Listen on " + Addr + " with TLS")

	server.Serve(ls)
}
  • 演示 

        成功: 

         失败:

        补充:

        google.golang.org/grpc/credentials/oauth 包已实现了用于 Google API oauth jwt 验证
的方法,使用方法可以参考 官方文档 。在实际应用中,我们可以根据自己的业务需求实现合适的验证方 式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/701822.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AMEYA360| 罗姆开发出新型二合一 SiC封装模块“TRCDRIVE pack™”

全球知名半导体制造商ROHM(总部位于日本京都市)面向300kW以下的xEV(电动汽车)用牵引逆变器,开发出二合一SiC封装型模块“TRCDRIVE pack™”,共4款产品(750V 2个型号:BSTxxxD08P4A1x4,1,200V 2个型号:BSTxxxD12P4A1x1)。…

深入理解Python多进程

目录 一、引言 二、Python多进程基础 进程与线程的区别 Python多进程模块 三、Python多进程实现原理 进程创建 进程间通信 进程同步 四、Python多进程使用方法 创建进程 进程间通信 五、实战案例 六、总结 一、引言 在Python编程中,多进程是一种重…

PartnerShare VS Tolt:出海SaaS选择哪种推广分销系统合适?

SaaS产品的成功在很大程度上取决于其推广策略的有效性。PartnerShare联盟系统和Tolt都是市场上比较知名的推广分销解决方案,能够帮助企业扩大用户基础并提高品牌知名度。 但是两款工具在某些特定任务上肯定有自己的独特优势,“找到你的锤子,…

SpringBoot-集成TOTP

TOTP验证码提供了一种高效且安全的身份验证方法。它不仅减少了依赖短信或其他通信方式带来的成本和延时,还通过不断变换的密码增加了破解的难度。未来,随着技术的进步和对安全性要求的提高,TOTP及其衍生技术将继续发展并被更广泛地应用。TOTP…

QT安装及项目创建

一、QT安装 1、安装qt_creater 方法一: 镜像文件:在2024-6-12:版本已经更新到了6.7 下载地址:https://download.qt.io/archive/qt/ 方法二: 百度网盘:链接:https://pan.baidu.com/s/1D0EmH…

SpringSecurity入门(一)

1、引入依赖 spring-boot版本2.7.3&#xff0c;如未特殊说明版本默认使用此版本 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><g…

【Linux】基础IO [万字之作]

目录 一.重谈文件 二.重谈C文件操作 1.操作 1.文件的打开和关闭 2.文件的读写操作 ​编辑 1.fgetc函数 2.fputc函数 3.fputs函数 4.fgets函数 5.fprintf函数 6.fscanf函数 7.fread函数 8.fwrite函数 三.重谈当前路径 四.系统文件操作接口 1.Open函数 2.write函数 3…

hot100 -- 栈

目录 &#x1f6a9;有效的括号 &#x1f33c;最小栈 AC 栈 AC 链表 &#x1f33c;字符串解码 &#x1f43b;每日温度 &#x1f352;柱状图中的最大矩形 解释 AC 单调栈 &#x1f6a9;有效的括号 20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 1&#xf…

[初阶数据结构] 包装类 | 泛型

目录 一. 包装类 1.1 什么是包装类? 1.2 包装类的意义 1.3 基本数据类型与包装类 1.4 装箱 1.5 拆箱 1.6 小总结 二. 泛型 2.1 什么是泛型? 2.2 泛型的意义 2.3 泛型的语法 2.4 泛型的编译 2.4.1 下载插件 2.4.2 分析 2.5 上界 2.6 泛型方法 2.7 小总结 三. 总结 一.…

conda虚拟环境,安装pytorch cuda cudnn版本一致,最简单方式

1、pytorch版本安装&#xff08;卸载也会有问题&#xff09; &#xff08;1&#xff09;版本如何选择参考和卸载 https://zhuanlan.zhihu.com/p/401931724 &#xff08;2&#xff09;对应版本如何安装命令 https://pytorch.org/get-started/previous-versions/ 最简答安装参考…

递推算法及相关问题详解

目录 递推的概念 训练&#xff1a;斐波那契数列 解析 参考代码 训练&#xff1a;上台阶 参考代码 训练&#xff1a;信封 解析 参考代码 递推的概念 递推是一种处理问题的重要方法。 递推通过对问题的分析&#xff0c;找到问题相邻项之间的关系&#xff08;递推式&a…

实验滤膜等分切割器八等分90mm

名称:滤膜切分器 型号: RNKF-90 适用范围:切分φ90mm玻璃纤维滤膜、石英纤维滤膜 等分数:2等分、4等分、8等分 使用方法: 1、开盖:逆时针旋转防尘盖&#xff0c;与切分台分开后&#xff0c;轻放于台面。 2、放膜:持专用镊子,镊子的长尖在下,短尖在上,取待切分滤膜1片,采样…

配置响应拦截器,全局前置导航守卫

1&#xff1a;配置响应拦截器 响应拦截器&#xff0c;统一处理接口的错误 问题&#xff1a;每次请求&#xff0c;都会有可能会错误&#xff0c;就都需要错误提示 说明&#xff1a;响应拦截器是咱们拿到数据的 第一个 数据流转站&#xff0c;可以在里面统一处理错误。 // 添…

uniapp小程序计算地图计算距离

我们拿到自身和目标距离经纬度 调用此方法即可计算出自身与目标的距离 最后我所展示的页面如下 具体效果可能会有点偏差 要求严格的可以在精细的计算一下

ant组件库日期选择器汉化

ant组件库日期选择器默认英文 如何汉化 跟着官网走不能完全实现汉化。 这里提供一个解决方案&#xff0c;首先&#xff0c;通过pnpm下载moment包。 然后引入和注册文件&#xff1a; import zhCN from ant-design-vue/es/locale/zh_CN;import moment from moment;moment.loca…

vue30:v-model语法糖的本质

在Vue.js框架中&#xff0c;v-model 是一个指令&#xff0c;用于在表单输入和应用状态之间创建双向数据绑定。它本质上是语法糖&#xff0c;意味着它提供了一种更简洁的方式来编写代码&#xff0c;而不需要显式地编写额外的代码。 具体来说&#xff0c;v-model 背后实际上是由…

外汇天眼:Equals集团发布战略评估通知:MDP不再考虑收购提议

Equals Group plc (LON)今天发布了一份关于其战略评估的通知。 Equals公司不再与Madison Dearborn Partners, LLC (MDP)就公司的收购提议进行讨论。MDP因此发布了一份声明&#xff0c;确认其不打算为公司提出收购提议。 然而&#xff0c;MDP与其投资组合公司MoneyGram Interna…

台式电脑怎么连WiFi?4个宝藏方法收藏好!

“我有一部台式电脑&#xff0c;现在不知道应该怎么操作才能让电脑正确连接WiFi&#xff0c;不知道大家有什么简单的连接方法吗&#xff1f;希望可以给我出出主意。” 随着无线网络的普及和科技的飞速发展&#xff0c;越来越多人选择使用WiFi来连接互联网。对于笔记本电脑和移动…

计算机网络(3) 字节顺序:网络字节序与IPv4

一.小端与大端 小端&#xff08;Little endian&#xff09;&#xff1a;低字节保存在内存低地址&#xff0c;高字节保存在内存高地址。 大端&#xff08;Big endian&#xff09;&#xff1a;低字节保存在内存高地址&#xff0c;高字节保存在内存低地址。 例如&#xff08;14…

Android 中USB-HID协议实现

前言 所有通过USB连接android设备进行通讯的步骤都是大同小异&#xff1a;查询usb设备列表 ——>匹配对应的设备类型&#xff08;如productid , vendorId&#xff09;等——>连接usb设备&#xff0c;找到连接通讯的节点——>配置通讯信息&#xff0c;进行通讯。以上是…