【gRPC】一元拦截器与流式拦截器确保请求附带认证令牌token

双端实现流式拦截器与 OAuth2 认证的逻辑步骤汇总

步骤客户端逻辑服务端逻辑
1. 获取令牌- 客户端通过 FetchToken 函数获取令牌,通常为 OAuth2 的访问令牌(Access Token)。- 服务端通过静态配置或动态服务获取合法的令牌(如 fetchToken()),作为验证基准。
2. 创建认证凭据- 使用令牌调用 GetPerRPCCredentials 封装成 credentials.PerRPCCredentials- 在请求到达服务端时,从元数据中提取 Authorization 字段并解析令牌内容。
3. 附加认证元数据- 在客户端流式拦截器中,判断是否已配置凭据,若没有,则通过 grpc.PerRPCCredentials 附加认证信息。- 在服务端流式拦截器中,从流上下文(ServerStream.Context)中提取元数据,用于后续校验。
4. 配置拦截器- 设置流式拦截器 StreamInterceptor,在每个流式 RPC 请求中附加认证信息。- 设置服务端流式拦截器 StreamInterceptor,在每个流式请求到达时校验身份认证信息。
5. 校验令牌- 客户端负责发送令牌,无需校验。- 服务端调用 oauth2Valid 校验令牌:
- 提取 authorization 元数据字段;
- 解析 Bearer 令牌;
- 调用 valid 验证令牌是否与合法令牌匹配。
6. 处理请求- 流式 RPC 请求携带认证凭据后,交由具体的业务逻辑处理。- 如果校验通过,调用 handler 继续处理业务逻辑;如果校验失败,返回错误拒绝请求。

逻辑说明

  1. 客户端

    • 通过 OAuth2 获取令牌,并将其封装成 credentials.PerRPCCredentials
    • 流式拦截器负责在每次请求中自动附加 Authorization 元数据,携带格式为 Bearer <token> 的令牌。
    • 认证元数据通过拦截器透明处理,业务代码无需感知。
  2. 服务端

    • 流式拦截器负责拦截每个流式请求。
    • 从流上下文提取 Authorization 元数据字段,解析令牌并校验合法性。
    • 校验通过

客户端需要在每次请求中附加令牌,可以通过拦截器实现。

package client

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
)

// 客户端需要在每次请求中附加令牌,可以通过拦截器实现。
// 两种拦截器的主要目的是确保每个请求(无论是一元请求还是流式请求)都附带上认证令牌(Token)

/*
在附加拦截器的主要作用是在每个单独的请求中附加 OAuth2 令牌。
1.检查是否已经配置
2.没有配置的话调用配置
3.invoke调用实际请求
*/

// UnaryInterceptor 一元请求拦截器
func UnaryInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
	fmt.Println("client UnaryInterceptor")
	var credsConfigured bool
	for _, opt := range opts {
		_, ok := opt.(*grpc.PerRPCCredsCallOption) // 检查是否已经有 PerRPCCredentials 被配置
		if ok {                                    // 检查令牌是否已经配置
			credsConfigured = true
			break
		}
	}
	if !credsConfigured {
		opts = append(opts, grpc.PerRPCCredentials(GetPerRPCCredentials(FetchToken()))) // 调用 FetchToken() 获取 OAuth2 令牌
	}

	return invoker(ctx, method, req, reply, cc, opts...)
}

// StreamInterceptor 流式拦截器  
func StreamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {


	fmt.Println("client StreamInterceptor")  
	var credsConfigured bool  
	for _, opt := range opts {  
		_, ok := opt.(*grpc.PerRPCCredsCallOption)  
		if ok {  
			credsConfigured = true  
			break  
		}
	}
	if !credsConfigured {  
		opts = append(opts, grpc.PerRPCCredentials(GetPerRPCCredentials(FetchToken())))


	}

	return streamer(ctx, desc, cc, method, opts...)  
}


客户端的token附加逻辑

package client

import (
	"golang.org/x/oauth2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/oauth"
)

/*
1.获取令牌 FetchToken
2.创建认证凭据 使用GetPerRPCCredentials,将string封装为PerRPCCredentials 用于gRPC身份认证
3.配置连接选项 GetAuth,将认证凭证添加到gRPC连接配置中
4.建立连接发送请求,grpc.Dial传入GetAuth认证选项
*/

// GetAuth
// 提供一个 gRPC 的 DialOption,用于在每个请求中自动附加 OAuth2 认证令牌。
// 通过调用 GetPerRPCCredentials,将 token 转换为 gRPC 可用的凭据。
func GetAuth(token string) grpc.DialOption {
	perRPC := GetPerRPCCredentials(token)
	return grpc.WithPerRPCCredentials(perRPC) // 创建一个 grpc.DialOption,可在建立 gRPC 连接时使用
}

// GetPerRPCCredentials
// 将静态令牌(token)封装为 gRPC 的 PerRPCCredentials 对象,这个对象会在每个请求中附带到元数据中。
func GetPerRPCCredentials(token string) credentials.PerRPCCredentials {
	// 使用 oauth2.StaticTokenSource 创建一个静态令牌源。
	//将静态令牌源封装为 gRPC 的 oauth.Token。
	//返回的对象会在每个 gRPC 请求中附加到元数据中,作为认证凭据。
	return oauth.TokenSource{TokenSource: oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})} // oauth2依赖下载:go get golang.org/x/oauth2
}

// FetchToken 模拟一个获取 OAuth2 令牌的方法,实际场景中应该从身份认证服务获取动态令牌
func FetchToken() string {
	return "some-secret-token"
}


服务端通过拦截器验证每个请求是否包含合法的令牌。

package server

import (
	"context"
	"errors"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"strings"
)

// 服务端通过拦截器验证每个请求是否包含合法的令牌。
// 两种拦截器的主要目的是确保每个请求(无论是一元请求还是流式请求)都附带上认证令牌(Token)

// server端作用:一个 gRPC 服务端的认证机制,通过一元请求拦截器和流式拦截器校验客户端请求中是否包含合法的身份认证令牌。

/*
1.客户端发起gRPC请求,在元数据中附加Authorization字段
2.服务端拦截器拦截
3.校验令牌
4.认证结果
*/

// UnaryInterceptor 一元请求拦截器
func UnaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
	fmt.Println("server UnaryInterceptor")
	fmt.Println(info)
	err = oauth2Valid(ctx)
	if err != nil {
		return nil, err
	}

	return handler(ctx, req)
}

// StreamInterceptor 流式拦截器
func StreamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
	fmt.Println("server StreamInterceptor")
	fmt.Println(info)
	err := oauth2Valid(ss.Context())
	if err != nil {
		return err
	}

	return handler(srv, ss)
}

func oauth2Valid(ctx context.Context) error {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return errors.New("元数据获取失败,身份认证失败")
	}
	authorization := md["authorization"]
	if !valid(authorization) {
		return errors.New("身份令牌校验失败,身份认证失败")
	}
	return nil
}

// 令牌验证逻辑
func valid(authorization []string) bool {
	if len(authorization) < 1 {
		return false
	}
	// 提取并校验 Bearer 令牌
	token := strings.TrimPrefix(authorization[0], "Bearer ")
	return token == fetchToken()
}

// 获取合法令牌
// 这是一个静态实现,实际应用中应从数据库或配置文件动态加载。
func fetchToken() string {
	return "some-secret-token"
}

客户端逻辑与流式拦截器实现

  1. 客户端通过 FetchToken 函数获取 OAuth2 令牌。
  2. 使用 GetPerRPCCredentials 函数将令牌封装为 grpc.PerRPCCredentials。
  3. 客户端流式拦截器 StreamInterceptor 判断是否附加了凭据,如果没有则添加。
  4. 配置流式拦截器,所有请求自动附带 Authorization 元数据(格式:Bearer )。
  5. 流式 RPC 请求携带认证信息后,正常发往服务端。

服务端逻辑与流式拦截器实现

  1. 服务端通过静态 fetchToken() 或动态服务获取合法的认证令牌。
  2. 服务端流式拦截器 StreamInterceptor 提取流上下文中的 Metadata。
  3. 调用 oauth2Valid 校验 Authorization 元数据:
    • 提取 Authorization 字段

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

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

相关文章

【pyqt】(四)Designer布局

布局 之前我们利用鼠标拖动的控件的时候&#xff0c;发现一些部件很难完成对齐这些工作&#xff0c;pyqt为我们提供的多种布局功能不仅可以让排版更加美观&#xff0c;还能够让界面自适应窗口大小的变化&#xff0c;使得布局美观合理。最常使用的三种布局就是垂直河子布局、水…

解决“KEIL5软件模拟仿真无法打印浮点数”之问题

在没有外部硬件支持时&#xff0c;我们会使用KEIL5软件模拟仿真&#xff0c;这是是仿真必须要掌握的技巧。 1、点击“Project”&#xff0c;然后点击“Options for target 项目名字”&#xff0c;点击“Device”,选择CPU型号。 2、点击“OK” 3、点击“Target”,勾选“Use Mi…

【项目实战1】五子棋游戏

目录 C语言编程实现五子棋&#xff1a;&#xff1a; game.h game.c 1.打印菜单 2.打印棋盘 3.玩家下棋 4.判断五子连珠 5.判断输赢 6.游戏运行 game.c完整源代码展示 test.c C语言编程实现五子棋&#xff1a;&#xff1a; game.h #pragma once #include<stdio.h> …

用ResNet50+Qwen2-VL-2B-Instruct+LoRA模仿Diffusion-VLA的论文思路,在3090显卡上训练和测试成功

想一步步的实现Diffusion VLA论文的思路&#xff0c;不过论文的图像的输入用DINOv2进行特征提取的&#xff0c;我先把这个部分换成ResNet50。 老铁们&#xff0c;直接上代码&#xff1a; from PIL import Image import torch import torchvision.models as models from torch…

Spring Boot 项目自定义加解密实现配置文件的加密

在Spring Boot项目中&#xff0c; 可以结合Jasypt 快速实现对配置文件中的部分属性进行加密。 完整的介绍参照&#xff1a; Spring Boot Jasypt 实现application.yml 属性加密的快速示例 但是作为一个技术强迫症&#xff0c;总是想着从底层开始实现属性的加解密&#xff0c;…

A/B实验之置信检验(一):如何避免误判 (I类) 和漏报 (II类)

假设检验的依据&#xff1a;如何避免误判和漏报 A/B实验系列相关文章&#xff08;置顶&#xff09; 1.A/B实验之置信检验&#xff08;一&#xff09;&#xff1a;如何避免误判和漏报 2.A/B实验之置信检验&#xff08;二&#xff09;&#xff1a;置信检验精要 引言 在数据驱动…

每日一题:链表中环的入口结点

文章目录 判断链表环的入口节点描述数据范围&#xff1a;复杂度要求&#xff1a;输入输出 示例代码实现思路解析注意事项&#xff1a; 判断链表环的入口节点 描述 给定一个链表&#xff0c;判断该链表是否存在环。如果存在环&#xff0c;返回环的入口节点&#xff1b;如果不存…

深度学习blog-Meanshift均值漂移算法-最大熵模型

均值漂移&#xff08;Mean Shift&#xff09;是一种无监督的聚类算法&#xff0c;广泛应用于数据挖掘和计算机视觉任务。它通过移动样本点到其近邻的均值位置来寻找数据的高密度区域&#xff0c;最终形成聚类。 均值漂移算法原理 均值漂移算法的核心思想是通过滑动窗口&#…

51c自动驾驶~合集45

我自己的原文哦~ https://blog.51cto.com/whaosoft/13020031 #运动控制和规划控制需要掌握的技术栈~ 各大垃圾家电造车厂又要开始了~~~​ 1、ROS的通信方式 李是Lyapunov的李&#xff1a;谈谈ROS的通信机制 话题通信和服务通信&#xff0c;其中话题通信是通过发布和订阅…

Python基于jieba和wordcloud绘制词云图

【Cesium】自定义材质&#xff0c;添加带有方向的滚动路线 &#x1f356; 前言&#x1f3b6;一、实现过程✨二、代码展示&#x1f3c0;三、运行结果&#x1f3c6;四、知识点提示 &#x1f356; 前言 Python基于jieba和wordcloud绘制词云图 &#x1f3b6;一、实现过程 读取文本…

计算机网络与服务器

目录 架构体系及相关知识 三层架构&#xff1a; 四层架构&#xff1a; 常见的应用的模式&#xff1a; OSI模型 分层 数据链路层 TCP/IP模型 TCP和UDP都是传输层的协议 TCP三次握手、四次次分手 URL&HTTP协议详解 网址URL 结构化 报文行 报文头 空行 报文体…

Cursor实现go项目配置并实现仓库Gin项目运行

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f49e;当前专栏&#xff1a;知识备份 ✨特色专栏&#xff1a;知识分享 &#x…

141.环形链表 142.环形链表II

141.环形链表 & 142.环形链表II 141.环形链表 思路&#xff1a;快慢指针 or 哈希表 快慢指针代码&#xff1a; class Solution { public:bool hasCycle(ListNode *head) {if(headnullptr||head->nextnullptr)return false;ListNode *fasthead->next; //不能设置成…

信用租赁系统助力企业实现免押金租赁新模式

内容概要 在现代商业环境中&#xff0c;信用租赁正在迅速崛起。通过结合大数据与区块链技术&#xff0c;信用租赁系统彻底改变了传统的租赁流程。什么是信用租赁呢&#xff1f;简单说&#xff0c;就是不需要押金&#xff0c;你也能够租到你想要的物品&#xff0c;这对企业和消…

el-select下拉框在弹框里面错位

问题出现 Element Plus 是一个基于 Vue 3 的组件库&#xff0c;el-select 是其中一个用于选择器的组件。在 el-select 组件中&#xff0c;teleported 属性用于控制下拉菜单的渲染位置。 解决方法 teleported 属性「element-plus」 popper-append-to-body属性「element」 ‌…

IO进程day1

一、思维导图

力扣-21-合并两个有序链表

思路&#xff1a; 因为是升序的两个链表&#xff0c;我们可以进行数据域比大小&#xff0c;然后把p3(自己创建的)的指针域指向小的那个 注&#xff1a;一定要先判断两个指针为0的情况

人工智能的发展领域之GPU加速计算的应用概述、架构介绍与教学过程

文章目录 一、架构介绍GPU算力平台概述优势与特点 二、注册与登录账号注册流程GPU服务器类型配置选择指南内存和存储容量网络带宽CPU配置 三、创建实例实例创建步骤镜像选择与设置 四、连接实例SSH连接方法远程桌面配置 一、架构介绍 GPU算力平台概述 一个专注于GPU加速计算的…

QT实现 端口扫描暂停和继续功能 3

上篇QT给端口扫描工程增加线程2-CSDN博客 为按钮pushButton_Stop添加clicked事件&#xff0c;功能为暂停扫描&#xff0c;并在暂停后显示继续按钮&#xff0c;点击继续按钮之后继续扫描 1.更新UI 添加继续按钮 点击转到槽则会自动声明 2. 更新 MainWindow.h 需要新增的部分…

汽车微处理器安全机制以及测试介绍

本文介绍了三类汽车微处理器安全机制&#xff1a;硬件类、软件类和混合类&#xff0c;旨在提高系统的可靠性和安全性。硬件类安全机制包括逻辑内建自测试&#xff08;Logic-BIST&#xff09;、三重模块冗余&#xff08;TMR&#xff09;、内存内建自测试&#xff08;Memory-BIST…