Golang——gRPC认证

news2024/12/26 20:41:19

一. 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.coloradmin.cn/o/1814570.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能售货机的成功关键点

智能售货机的成功关键点 智能售货机的盈利水平是众多投资者关注的焦点。尽管常有人认为该行业利润丰厚,但实际上,智能售货机的利润率通常维持在一个相对适中的范围,大约在5%至15%之间。这一数据背后,涵盖了包括物流配送、日常运维…

Java---BigInteger和BigDecimal和枚举

1.简介 1.BigInteger可以支持任意长度的整数 2.BigDecimal可以支持任意精度的浮点数 3.用来做精确计算 2.创建方式 new BigInteger(); new BigInteger(参数1,进制):可以将不同进制转成10进制显示 new BigDecimal(); BigInteger.valueOf(); BigDecimal.valueOf();…

transformers之text generation解码策略

目录 参数TemperatureTop-p and Top-k1. 选择最上面的token:贪婪解码2. 从最上面的tokens中选择:top-k3. 从概率加起来为15%的top token中选择:top-pFrequency and Presence Penaltiestransformers库中的解码策略贪婪搜索对比搜索多项式采样beam搜索解码beam搜索多项式采样多样…

中国大模型站起来了!甚至被美国团队反向抄袭

一直以来,美国是公认的AI领域强者,我国AI技术虽然差不多,但始终落人一步。然而,近日斯坦福团队的AI模型却被指控抄袭中国AI模型,这下许多人都坐不住了。 被实锤抄袭的,是斯坦福大学AI团队,他们…

WWDC 2024及其AI功能的引入对中国用户和开发者的影响

WWDC(Apple Worldwide Developers Conference)是苹果公司一年一度的重要活动,吸引了全球开发者的关注。WWDC 2024引入了许多新技术和功能,尤其是AI功能的加入,引发了广泛讨论。本文将深入探讨中国开发者如何看待WWDC 2…

四川赤橙宏海商务信息咨询有限公司揭秘抖音电商新风口

在数字化浪潮席卷全球的今天,电商行业作为新时代经济的生力军,正以前所未有的速度发展。作为抖音电商服务的佼佼者,四川赤橙宏海商务信息咨询有限公司凭借其专业的服务团队和前瞻的市场洞察,不断刷新行业纪录,助力商家…

docker安装rabbitmq和延迟插件(不废话版)

1.下载镜像 docker pull rabbitmq:3.8-management 2.启动 docker run -e RABBITMQ_DEFAULT_USERlicoos -e RABBITMQ_DEFAULT_PASSlicoosrabbitmq -v mq-plugins:/plugins --name mq --hostname mq -p 15672:15672 -p 5672:5672 -d rabbitmq:3.8-management 3.下载对…

跨海交流丨台湾混凝土行业参访团与上海思伟软件共筑“智慧砼厂”梦 !

每一次跨越地域的握手 都是行业革新与智慧交融的序曲 台湾优质混凝土参访团 2024年5月29日,财团法人台湾营建研究院院长吕良正先生,率领着由61名行业精英组成的台湾商砼参访团,跨越海峡抵达上海,开展了一场连接两岸的学习交流活动…

苹果的股票都飙升7%了,谷歌仍在建议你往披萨上加胶水|TodayAI

最近,谷歌(Google)的人工智能再次引发了一场笑话。这次,它建议用户在披萨上添加胶水,引起了广泛关注和讨论。事情的起因源自一位互联网传奇人物Katie Notopoulos,她实际上制作并食用了一个胶水披萨&#xf…

【源码】2024最新陪诊小程序uniapp+thinkphp

20 2024最新陪诊小程序uniappthinkphp资源来源:52codes.cc 20最新陪诊小程序uniappthinkphp 简介:随着社会逐渐步进入老龄化越来越多的老年人或者不经常去医院的用户对于医院繁琐的流程很是苦劳于是陪诊这个行业开始兴起。小白陪诊开发理念&#xff0…

PS2045L-ASEMI低Low VF肖特基PS2045L

编辑:ll PS2045L-ASEMI低Low VF肖特基PS2045L 型号:PS2045L 品牌:ASEMI 封装:TO-277 最大平均正向电流(IF):20A 最大循环峰值反向电压(VRRM):45V 最大…

CVE-2019-20933-influxdb未授权访问-vulhub

1.原理 参考:https://blog.csdn.net/tqlisno1/article/details/109110644 InfluxDB 未授权访问 漏洞复现_influxdb未授权访问复现-CSDN博客 InfluxDB 是一个开源分布式时序、时间和指标数据库,使用 Go 语言编写,无需外部依赖。其设计目标是…

铝合金板件加工迎来3D视觉新时代

在制造业的浩瀚星空中,铝合金板件加工一直以其轻质、高强度、耐腐蚀的特性,扮演着举足轻重的角色。然而,随着市场竞争的加剧和产品需求的多样化,传统的加工方式已难以满足现代制造业对高效率、高精度的追求。在这个关键时刻&#…

【Java】解决Java报错:ArithmeticException during Division

文章目录 引言一、ArithmeticException的定义与概述1. 什么是ArithmeticException?2. ArithmeticException的常见触发场景3. 示例代码 二、解决方案1. 检查除数是否为零2. 使用异常处理3. 使用浮点数除法4. 使用自定义方法进行安全除法 三、最佳实践1. 始终检查除数…

RAG实操教程,LangChain + Llama2 | 创造你的个人LLM

本文将逐步指导您创建自己的RAG(检索增强生成)系统,使您能够上传自己的PDF文件并向LLM询问有关PDF的信息。本教程侧重于图中蓝色部分,即暂时不涉及Gradio(想了解已接入Gradio的,请参考官网)。相…

计算机网络-BGP路由优选原则四-优选AS_Path属性值最短的路由

一、优选AS_Path属性值最短的路由 AS_Path:这是BGP中最重要的属性之一,它记录了路由信息经过的所有自治系统。AS_Path属性帮助接收路由信息的路由器了解该路由的来源和路径。AS_Path由一系列的自治系统号组成,这些自治系统号代表了路由信息在…

SAP PP学习笔记17 - MTS(Make-to-Stock) 按库存生产 的策略70,策略59

上几章讲了几种策略,策略10,11,30,40。 SAP PP学习笔记14 - MTS(Make-to-Stock) 按库存生产(策略10),以及生产计划的概要-CSDN博客 SAP PP学习笔记15 - MTS(Make-to-St…

架构设计-跨域问题的根源及解决方式

前面文章《架构设计-web项目中跨域问题涉及到的后端和前端配置》中说明了处理跨域问题的一种方式,本文详细说明下产生跨域问题的原因及处理方式。 一、产生跨域问题的原因: 浏览器的同源策略:这是跨域问题的根本原因。同源策略是浏览器对Jav…

C语言 sizeof 和 strlen

目录 一、sizeof 和 strlen 的区别 a.sizeof b.strlen c.sizeof与strlen的区别 二、数组和指针笔试题解析(32位环境) a.一维数组( int a[ ] { 1 , 2 , 3 , 4 } ) b.字符数组 (char arr[ ] {a , b , c , d , e , f }) ( char arr[ …

吴恩达2022机器学习专项课程C2W3:实验Lab_01模型评估与选择

这里写目录标题 导入模块与实验环境配置回归1.构建并可视化数据集2.分割数据集3.重新绘制数据集3.特征缩放4.评估模型:计算训练集的误差5.评估模型:计算交叉验证集的误差 添加多项式1.构建多项式特征集2.缩放特征3.使用标准化的计训练集和交叉验证集&…