Golang——gRPC认证
一. OpenSSL
1.1 介绍
OpenSSL是一个开放源代码的软件库包,用于支持网络通讯过程中的加密。这个库提供的功能包含了SSL和TLS协议的实现,并可用于生成密钥、证书、进行密码运算等。
其组成主要包括一下三个组件:
-
openssl:多用途的命令行工具
-
libcrypto:加密算法库
-
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 mainimport ("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))//注册HelloServicehello.RegisterHelloServer(server, HelloService)fmt.Println("Listen on" + Addr + "with TLS")//这里面才会连接客户端,需要进行认证server.Serve(ls)
}
-
credentials.NewServerTLSFromFile
:从输入证书文件和密钥文件为服务端构造TLS凭证 -
grpc.Creds
:返回一个ServerOption,用于设置服务器连接的凭证。
客户端代码:
package mainimport ("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 mainimport ("context""fmt"hello "sample-app/grpc/proto""google.golang.org/grpc""google.golang.org/grpc/credentials"
)const (Addr = "127.0.0.1:8080"//是否使用TLSOpenTLS = true
)// 自定义认证
type Token struct {Appid stringAppkey 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.DialOptionif 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 mainimport ("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 stringvar appkey stringvals := md.Get("appid")if len(vals) != 0 {appid = vals[0]}val_key := md.Get("appkey")if len(val_key) != 0 {appkey = val_key[0]}//认证tokenif 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))//注册HelloServicehello.RegisterHelloServer(server, HelloService)fmt.Println("Listen on " + Addr + " with TLS")server.Serve(ls)
}
- 演示
成功:
失败:
补充:
google.golang.org/grpc/credentials/oauth 包已实现了用于 Google API 的 oauth 和 jwt 验证的方法,使用方法可以参考 官方文档 。在实际应用中,我们可以根据自己的业务需求实现合适的验证方 式。
相关文章:

Golang——gRPC认证
一. OpenSSL 1.1 介绍 OpenSSL是一个开放源代码的软件库包,用于支持网络通讯过程中的加密。这个库提供的功能包含了SSL和TLS协议的实现,并可用于生成密钥、证书、进行密码运算等。 其组成主要包括一下三个组件: openssl:多用途的命…...
探索FPGA技术:零成本入门指南
FPGA作为一门前沿的集成电路技术,不仅在数字电子领域有着广泛的应用,而且对于硬件编程和数字电路设计的学习者来说,是一次深入了解技术原理的绝佳机会。现在,让我们看看如何不花一分钱,就能开始我们的FPGA学习之旅。 方…...
Java数据结构与算法(组合问题回溯算法)
前言 上期重点介绍了回溯算法在约束满足问题情况下应用。这期看看在组合问题场景下如何使用。 回溯算法通常用于解决以下几类问题: 1. 组合问题 需要从集合中选择一些元素,并找出所有可能的组合。例子:子集生成问题、组合数问题ÿ…...

CMake的使用方法
1 CMakeLists.txt编写 cmake_minimum_required(VERSION 3.12)project(djl_plm)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdc17 -g")add_executable(simple simple.cpp) add_executable(main main.cpp)include_directories(include) 相当于如下gcc命令࿱…...

java面试整合全套
什么是Java (定义 优点) java是一个平台,由jvm和Java应用编程接口构成的一门面向编程语言。 不仅吸收了C语言的各种优点,还摒弃了c语言里面的多继承,指针等概念,因此java的特征主要有功能强大和简单易用的特征。 jav…...

贪吃蛇小游戏简单制作-C语言
文章目录 游戏背景介绍实现目标适合人群所需技术浅玩Window API什么是API控制台程序窗口大小,名称设置 Handle(句柄)获取句柄 坐标结构体设置光标位置 光标属性获取光标属性设置光标属性 按键信息获取 贪吃蛇游戏设计游戏前的初始化设置窗口的大小和名称本地化设置 宽字符Waht …...
Oracle数据库-重点信息查询方法
文章目录 一、数据库信息及查询方法1.1是否为RAC1.2 数据库存储容量大小1.3 在线会话数1.4 最大分区数1.5 最大存储过程行数1.6 单表最大行数1.7 最大单表大小1.8 表总数量1.9 无主键表的数量1.10 字段数超过200的宽表1.11 关注CPU耗时高的SQL 一、数据库信息及查询方法 1.1是…...

【全开源】多平台租房系统源码(Fastadmin+ThinkPHP+Uniapp)
🏠多平台租房系统:一站式租房新体验🔍 🌐一、引言:租房市场的变革 在快节奏的现代生活中,租房已成为许多人解决居住问题的首选。然而,传统的租房方式往往繁琐且效率低下。随着互联网的飞速发展…...
Pythond 的 corr函数
Python corr函数科普 在数据分析和机器学习领域,数据的相关性是一个非常重要的概念。相关性可以帮助我们理解数据之间的关系,并且可以作为一种预测模型的基础。Python中的corr()函数是一个用于计算数据之间相关性的强大工具。本文将介绍corr()函数的使用方法,并通过代码示例…...

Fiddler 中文版 (强大的网络响应HTPP协议抓包工具)
前言 Fiddler Web Debugger,功能强大的抓包工具,Web调试工具,HTTP协议抓包调试工具。它能够捕获浏览器和程序的所有http/https通信连接,可以针对访问请求,分析请求数据报文、设置断点、调试web程序、解密和美化JS脚本…...
初出茅庐的小李博客之JSON格式介绍
什么是JSON JSON:JavaScript Object Notation (翻译就是JavaScript 对象表示法),是一种表示对象的方法。 JSON 是存储和交换文本信息的语法,类似 XML。但是JSON 比 XML 更小、更快,更易解析。此外JSON也易于人阅读和编写。而且主流的编程语言…...

Vue3相关语法内容,组件传值,事件监听,具名插槽。
1、Vue3相关语法内容 赋值语句(ref、reactive系列)组件传值(父子,子父)watch,watchEffect监听slot具名插槽 1、赋值语法(ref,reactive) 1.1、ref 、isRef、 shallowRef、triggerRef、customRef 支持所有的类型&…...

Linux用户,用户组,所有者权限分配,sftp用户权限分配
注意以下命令执行需要在root用户下执行 tenant命令切换至root命令 sudo -do root 删除用户信息 1.不删除用户主目录 userdel user_name 2.删除用户主目录 userdel -r user_name usermod命令修改用户账户权限 更改用户名 sudo usermod -l newusername oldusername 更…...

iFlyCode:AI智能编程助手引领未来软件开发新趋势
体验地址 在当前软件行业飞速发展的背景下,开发效率和代码质量成为了衡量软件工程师工作效能的两大关键指标。为了应对日益增长的市场需求和紧迫的发布时间,科大讯飞推出了iFlyCode2.0——一款集AI技术于一身的智能编程助手,旨在引领未来软件…...
高低温测试发现文件被篡改
背景 高低温测试-40度和85度压测,出现程序崩溃现象(挂测日志看)。设备常温后也无法恢复,重启后也无法恢复。 定位排查 先校验程序资源文件一致性是否正确 1.取出设备中的程序资源,包括执行文件和主要的so文件(可以从大的文件开始) 2.…...
高考真的不再重要了吗?
阅读本文大概需要 1.11 分钟 一年一度的高考又落幕了,看到不少人说今年的高考热度好像少了几分,不再像过去那样热闹。于是就有人纳闷,高考是不是不那么重要了。 其实你觉得高考不重要,可能是因为你家今年没考生。就像你不怎么关注…...

spring常用注解(八)@Async
一、介绍 1、介绍 二、原理 三、集成与使用 1、集成方法 (1)开启 使用以下注解开启 EnableAsync (2)使用 在需要异步处理的方法上加上 Async 2、返回值 Async注解的方法返回值只能为void或者Future<T>。 &…...

B站画质补完计划(3):智能修复让宝藏视频重焕新生
1 老片存在什么画质问题? B站作为一个拥有浓厚人文属性的平台社区,聚集了诸如《雍正王朝》、《三国演义》等经典影视剧集,同时也吸引了大量用户欣赏、品鉴这些人文经典 。但美中不足的是,由于拍摄年代久远、拍摄设备落后、数据多次…...

Spring Cloud Stream整合RocketMQ
Spring Cloud Stream整合RocketMQ 这里书接上回,默认你已经搭建好了RocketMQ主从异步集群,前面文章已经介绍过搭建方法。 1、Spring Cloud Stream介绍 Spring Cloud Stream是一个框架,用于构建与共享消息系统连接的高度可扩展的事件驱动微服…...
Web前端浪漫源码:编织梦想与爱的交织乐章
Web前端浪漫源码:编织梦想与爱的交织乐章 在数字世界的广袤宇宙中,Web前端浪漫源码犹如一段段秘密的旋律,编织着梦想与爱的交织乐章。它们不仅是技术的结晶,更是情感的载体,将浪漫与创意融入每一个像素和每一行代码之…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...