首页 > 其他分享 >下一代云原生网关Higress:基于Wasm开发JWT认证插件

下一代云原生网关Higress:基于Wasm开发JWT认证插件

时间:2024-01-20 17:05:10浏览次数:27  
标签:Body 插件 HTTP 请求 应答 JWT 网关 higress go

什么是Higress

Higress是基于阿里内部的Envoy Gateway实践沉淀、以开源Istio + Envoy为核心构建的下一代云原生网关,实现了流量网关 + 微服务网关 + 安全网关三合一的高集成能力,深度集成Dubbo、Nacos、Sentinel等微服务技术栈,能够帮助用户极大的降低网关的部署及运维成本且能力不打折;在标准上全面支持Ingress与Gateway API,积极拥抱云原生下的标准API规范;同时,Higress Controller也支持Nginx Ingress平滑迁移,帮助用户零成本快速迁移到Higress。 image.png

什么是WASM

WASM代表"WebAssembly",它是一种可移植、低级别的二进制指令格式,旨在作为Web浏览器中的一种新型执行环境而推出。它的设计目标是为Web上的高性能应用提供一种通用的编译目标,使开发人员能够在不同平台和架构上运行高效的代码。 WASM的主要特点包括:

  1. 性能: WASM是一种二进制格式,可以高效地编码和解码,从而在浏览器中实现比传统JavaScript更快的执行速度。这使得WASM特别适用于需要高性能的Web应用,如游戏、图像处理和模拟器。
  2. 安全性: 由于WASM是低级别的虚拟机,它提供了一种隔离和受控的执行环境。这意味着它可以在浏览器中安全地运行,防止恶意代码对用户计算机的损害。
  3. 跨平台: WASM可以在不同的体系结构和操作系统上运行,使开发人员能够编写一次代码,然后在多个平台上运行,而无需进行大量的修改和调整。
  4. 语言无关性: 尽管WASM可以从多种编程语言中生成,但它与特定的编程语言无关。这使得开发人员能够选择他们熟悉的语言,并将其编译成WASM以在Web上运行。
  5. 适用范围: 虽然WASM主要针对Web浏览器中的Web应用程序,但它不仅限于此。它还可以在其他领域,如服务器端、嵌入式系统等中发挥作用。

最佳实践

言归正传,现在我们基于Wasm为Higress开发一个JWT认证插件,实现在Higress中进行token解析认证,如果Token无效或不存在直接拒绝返回401,Token有效继续访问后端微服务。利用最佳实践的方式带大家熟悉一下基于Wsam开发Higress插件的整个过程,流程图如下: openresty+lua解析Token.png 这样做的好处:

  • 下游微服务无需重复进行认证,减少了重复的工作,也提高了系统的安全性
  • 避免了下游服务重复解析token来获取用户信息的需求。减少了不必要的开销

Higress 部署

# 环境为Kubernetes v1.27.3
$ kubectl get nodes
NAME                 STATUS   ROLES           AGE   VERSION
kind-control-plane   Ready    control-plane   9h    v1.27.3

# 通过Helm安装
$ helm repo add higress.io https://higress.io/helm-charts

"higress.io" already exists with the same configuration, skipping
$ helm install higress -n higress-system higress.io/higress --create-namespace --render-subchart-notes --set global.local=true --set higress-console.o11y.enabled=false  --set higress-console.domain=console.higress.io --set higress-console.admin.password.value=admin

NAME: higress
LAST DEPLOYED: Thu Aug 10 20:37:40 2023
NAMESPACE: higress-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Higress successfully installed!

To learn more about the release, try:
  $ helm status higress -n higress-system
  $ helm get all higress -n higress-system

1. Use the following URL to access the console:
  http://console.higress.io/
  Since Higress Console is running in local mode, you may need to add the following line into your hosts file before accessing the console:
  127.0.0.1 console.higress.io
2. Use following commands to get the credential and login:
  export ADMIN_USERNAME=$(kubectl get secret --namespace higress-system higress-console -o jsonpath="{.data.adminUsername}" | base64 -d)
  export ADMIN_PASSWORD=$(kubectl get secret --namespace higress-system higress-console -o jsonpath="{.data.adminPassword}" | base64 -d)
  echo -e "Username: ${ADMIN_USERNAME}\nPassword: ${ADMIN_PASSWORD}"
  NOTE: If this is an upgrade release, your current password won't be changed.
3. If you'd like to change the credential, you can edit this secret with new values: higress-system/higress-console

# 查看密码
$ export ADMIN_USERNAME=$(kubectl get secret --namespace higress-system higress-console -o jsonpath="{.data.adminUsername}" | base64 -d)
$ export ADMIN_PASSWORD=$(kubectl get secret --namespace higress-system higress-console -o jsonpath="{.data.adminPassword}" | base64 -d)
$ echo -e "Username: ${ADMIN_USERNAME}\nPassword: ${ADMIN_PASSWORD}"
Username: admin
Password: admin

# 配置Hosts
 $ cat /etc/hosts
 127.0.0.1 demo.kubesre.com console.higress.io

# 转发一下端口本地可以访问
$ kubectl  port-forward service/higress-gateway -n higress-system 80:80

访问地址:http://console.higress.io/plugin Username: admin Password: admin

image.png 出现如上界面说明安装成功!

开发环境搭建

以MacOS为例,Windows可以去查阅官方文档进行安装环境: Golang:https://go.dev/doc/install TinyGo:https://tinygo.org/getting-started/install/ Wasm-opt:https://github.com/WebAssembly/binaryen.git

# 安装golang
$ brew install go
$ go version
go version go1.19.9 darwin/arm64

# TinyGo
$ wget https://github.com/tinygo-org/tinygo/releases/download/v0.25.0/tinygo0.25.0.darwin-amd64.tar.gz
$ tar -zxf tinygo0.25.0.darwin-amd64.tar.gz
$ export PATH=/tmp/tinygo/bin:$PATH
$ tinygo version
tinygo version 0.28.0-dev-5c2753e darwin/amd64 (using go version go1.19.9 and LLVM version 15.0.0)

# Wasm-opt
$ wget https://github.com/WebAssembly/binaryen/releases/download/version_114/binaryen-version_114-arm64-macos.tar.gz
$ tar binaryen-version_114-arm64-macos.tar.gz
$ export PATH=/tmp/binaryen/bin:$PATH
$ wasm-opt --version
wasm-opt version 114 (version_114)

到此为止,开发环境搭建完毕:

JWT认证插件开发

初始化工程:

# 初始化工程
$  mkdir jwt-plugin
$  cd jwt-plugin 
$  go mod init jwt-plugin 
go: creating new go.mod: module jwt-plugin
# 设置代理
$ go env -w GOPROXY=https://proxy.golang.com.cn,direct
# 下载依赖性
$ go get github.com/tetratelabs/proxy-wasm-go-sdk
$ go get github.com/alibaba/higress/plugins/wasm-go@main
$ go get github.com/tidwall/gjson

插件代码:

package main

import (
	"encoding/json"
	"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
	jwt "github.com/dgrijalva/jwt-go"
	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
	"github.com/tidwall/gjson"
)

// 自定义插件配置

func main() {
	wrapper.SetCtx(
		"jwt-plugin",  // 配置插件名称
		wrapper.ParseConfigBy(parseConfig),
		wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
	)
}

type Config struct {
	TokenSecretKey string // 解析Token SecretKey
	TokenHeaders   string // 定义获取Token请求头名称
}

type Res struct {
	Code int    `json:"code"`  // 返回状态码
	Msg  string `json:"msg"`  // 返回信息
}

func parseConfig(json gjson.Result, config *Config, log wrapper.Log) error {
	// 解析出配置,更新到config中
	config.TokenSecretKey = json.Get("token_secret_key").String()
	config.TokenHeaders = json.Get("token_headers").String()
	return nil
}

func onHttpRequestHeaders(ctx wrapper.HttpContext, config Config, log wrapper.Log) types.Action {
	var res Res
	if config.TokenHeaders == "" || config.TokenSecretKey == "" {
		res.Code = 401
		res.Msg = "参数不足"
		data, _ := json.Marshal(res)
		_ = proxywasm.SendHttpResponse(401, nil, data, -1)
		return types.ActionContinue
	}

	token, err := proxywasm.GetHttpRequestHeader(config.TokenHeaders)  // 获取Token
	if err != nil {
		res.Code = 401
		res.Msg = "认证失败"
		data, _ := json.Marshal(res)
		_ = proxywasm.SendHttpResponse(401, nil, data, -1)
		return types.ActionContinue
	}
	valid := ParseTokenValid(token, config.TokenSecretKey)
	if valid {
		_ = proxywasm.ResumeHttpRequest()
		return types.ActionPause
	} else {
		res.Code = 401
		res.Msg = "认证失败"
		data, _ := json.Marshal(res)
		_ = proxywasm.SendHttpResponse(401, nil, data, -1)
		return types.ActionContinue
	}
}
// 认证解析Token
func ParseTokenValid(tokenString, TokenSecretKey string) bool {
	token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		// 在这里提供用于验证签名的密钥
		return []byte(TokenSecretKey), nil
	})
	return token.Valid
}

HTTP 处理挂载点: 代码中通过 wrapper.ProcessRequestHeadersBy将自定义函数 onHttpRequestHeaders用于HTTP 请求头处理阶段处理请求。除此之外,还可以通过下面方式,设置其他阶段的自定义处理函数

HTTP 处理阶段 触发时机 挂载方法
HTTP 请求头处理阶段 网关接收到客户端发送来的请求头数据时 wrapper.ProcessRequestHeadersBy
HTTP 请求 Body 处理阶段 网关接收到客户端发送来的请求 Body 数据时 wrapper.ProcessRequestBodyBy
HTTP 应答头处理阶段 网关接收到后端服务响应的应答头数据时 wrapper.ProcessResponseHeadersBy
HTTP 应答 Body 处理阶段 网关接收到后端服务响应的应答 Body 数据时 wrapper.ProcessResponseBodyBy

工具方法: 代码中的 proxywasm.SendHttpResponse是插件 SDK 提供的两个工具方法

分类 方法名称 用途 可以生效的HTTP 处理阶段
请求头处理 GetHttpRequestHeaders 获取客户端请求的全部请求头 HTTP 请求头处理阶段
ReplaceHttpRequestHeaders 替换客户端请求的全部请求头 HTTP 请求头处理阶段
GetHttpRequestHeader 获取客户端请求的指定请求头 HTTP 请求头处理阶段
RemoveHttpRequestHeader 移除客户端请求的指定请求头 HTTP 请求头处理阶段
ReplaceHttpRequestHeader 替换客户端请求的指定请求头 HTTP 请求头处理阶段
AddHttpRequestHeader 新增一个客户端请求头 HTTP 请求头处理阶段
请求 Body 处理 GetHttpRequestBody 获取客户端请求 Body HTTP 请求 Bod下一代云原生网关Higress:基于Wasm开发JWT认证插件y 处理阶段
AppendHttpRequestBody 将指定的字节串附加到客户端请求 Body 末尾 HTTP 请求 Body 处理阶段
PrependHttpRequestBody 将指定的字节串附加到客户端请求 Body 的开头 HTTP 请求 Body 处理阶段
ReplaceHttpRequestBody 替换客户端请求 Body HTTP 请求 Body 处理阶段
应答头处理 GetHttpResponseHeaders 获取后端响应的全部应答头 HTTP 应答头处理阶段
ReplaceHttpResponseHeaders 替换后端响应的全部应答头 HTTP 应答头处理阶段
GetHttpResponseHeader 获取后端响应的指定应答头 HTTP 应答头处理阶段
RemoveHttpResponseHeader 移除后端响应的指定应答头 HTTP 应答头处理阶段
ReplaceHttpResponseHeader 替换后端响应的指定应答头 HTTP 应答头处理阶段
AddHttpResponseHeader 新增一个后端响应头 HTTP 应答头处理阶段
应答 Body 处理 GetHttpResponseBody 获取客户端请求 Body HTTP 应答 Body 处理阶段
AppendHttpResponseBody 将指定的字节串附加到后端响应 Body 末尾 HTTP 应答 Body 处理阶段
PrependHttpResponseBody 将指定的字节串附加到后端响应 Body 的开头 HTTP 应答 Body 处理阶段
ReplaceHttpResponseBody 替换后端响应 Body HTTP 应答 Body 处理阶段
HTTP 调用 DispatchHttpCall 发送一个 HTTP 请求 -
GetHttpCallResponseHeaders 获取 DispatchHttpCall 请求响应的应答头 -
GetHttpCallResponseBody 获取 DispatchHttpCall 请求响应的应答 Body -
GetHttpCallResponseTrailers 获取 DispatchHttpCall 请求响应的应答 Trailer -
直接响应 SendHttpResponse 直接返回一个特定的 HTTP 应答 -
流程恢复 ResumeHttpRequest 恢复先前被暂停的请求处理流程 -
ResumeHttpResponse 恢复先前被暂停的应答处理流程 -

编译打包插件

# 编译WASM
$  tinygo build -o main.wasm -scheduler=none -target=wasi -gc=custom -tags='custommalloc nottinygc_finalizer' main.go

# 查看Dockerfile
cat Dockerfile 
FROM scratch
COPY main.wasm plugin.wasm

# 编译Docker Image
$ docker build -t  registry.cn-shanghai.aliyuncs.com/kubesre01/jwt-plugin:v1 .

[+] Building 0.1s (5/5) FINISHED                                                                                                                                                                               docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                                                                                           0.0s
 => => transferring dockerfile: 113B                                                                                                                                                                                           0.0s
 => [internal] load .dockerignore                                                                                                                                                                                              0.0s
 => => transferring context: 2B                                                                                                                                                                                                0.0s
 => [internal] load build context                                                                                                                                                                                              0.0s
 => => transferring context: 1.99MB                                                                                                                                                                                            0.0s
 => [1/1] COPY main.wasm plugin.wasm                                                                                                                                                                                           0.0s
 => exporting to image                                                                                                                                                                                                         0.0s
 => => exporting layers                                                                                                                                                                                                        0.0s
 => => writing image sha256:6097b8347fc71e52eadd05753b2bd6877f4f9283a9cd4d844e62259ef714e373                                                                                                                                   0.0s
 => => naming to registry.cn-shanghai.aliyuncs.com/kubesre01/jwt-plugin:v1 

 # 上传镜像到仓库
 $ docker push  registry.cn-shanghai.aliyuncs.com/kubesre01/jwt-plugin:v1                            
The push refers to repository [registry.cn-shanghai.aliyuncs.com/kubesre01/jwt-plugin]
31f0fa5f1adf: Pushed 
v1: digest: sha256:c3c629576c71d3dbd4d2bd8118c5523346c63cff7853ce995eb5879affd2c752 size: 526
 

部署示例服务

  • Auth服务:负责用户登陆授权返回Token
  • Demo服务:负责认证通过后访问的微服务
# 部署Auth服务
$ cat auth.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: auth
  labels:
    app: auth
spec:
  replicas: 1
  selector:
    matchLabels:
      app: auth
  template:
    metadata:
      labels:
        app: auth
    spec:
      containers:
      - name: auth
        imagePullPolicy: Always
        image: registry.cn-shanghai.aliyuncs.com/kubesre01/auth:v1
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: auth-svc
spec:
  type: ClusterIP
  selector:
    app: auth
  ports:
    - port: 8080
      targetPort: 8080
$ kubectl apply -f auth.yml
deployment.apps/auth unchanged
service/auth-svc unchanged


# 部署demo服务
$  cat demo.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  labels:
    app: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - name: demo
        imagePullPolicy: Always
        image: registry.cn-shanghai.aliyuncs.com/kubesre01/demo:v1
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: demo-svc
spec:
  type: ClusterIP
  selector:
    app: demo
  ports:
    - port: 8080
      targetPort: 8080

$ kubectl apply -f demo.yml
deployment.apps/demo unchanged
service/demo-svc unchanged

验证JWT认证插件

安装插件: image.png

配置域名: image.png 配置路由: image.png image.png 为demo服务配置策略: image.png 验证: 先通过auth服务接口获取到Token image.png 携带Token进行请求demo服务,正常返回 image.png 不携带Token进行请求demo服务,返回401,详情如下 image.png

总结

本文带大家了解Higress云原生网关与Wasm,并通过最佳实践开发了基于Wasm的Higress JWT认证插件的整个过程,相信大家一定有所收获,接下来文章内容中会分享更多企业级实战案例,请敬请期待!

标签:Body,插件,HTTP,请求,应答,JWT,网关,higress,go
From: https://blog.51cto.com/u_14136800/9345755

相关文章

  • 金蝶云星空创建表单插件项目
     一、新建一个空的解决方案  二、新建一个类库     三、添加引用添加金蝶安装目录的引用,如果是协同开发,那就是使用工作空间下的dll。本地金蝶云安装目录:C:\ProgramFiles(x86)\Kingdee\K3Cloud\WebSite\bin协同开发工作目录:D:\WorkSpace\XXXX\mm_k3Cloud\K3......
  • 兴达易控EtherCAT主站转Profinet网关带你轻松实现设备互通
    兴达易控EtherCAT主站转Profinet网关带你轻松实现设备互通XD-ETHPNM20是一款EtherCAT主站功能的通讯网关。EtherCAT主站转Profinet网关主要功能是将EtherCAT的从站设备(伺服、阀门、仪表、变频器等)接入到Profinet网络。XD-ETHPNM20网关连接到EtherCAT总线中作为主站使用,连接到Prof......
  • centOS7安装GLPI 和fusioninventory插件
    centOS7安装GLPI,一、关闭防火墙(不关闭只能本机访问):systemctlstopfirewalld.service#停止firewalld服务systemctldisablefirewalld.service#设置开机默认不启动#生产环境单独在防火墙上开启端口和策略#firewall-cmd--permanent--zone=public--add-port=80/tcp#firewa......
  • DB107-ASEMI插件小方桥DB107
    编辑:llDB107-ASEMI插件小方桥DB107型号:DB107品牌:ASEMI正向电流(Id):1A反向耐压(VRRM):1000V正向浪涌电流:50A正向电压(VF):1.05V引脚数量:4芯片个数:4芯片尺寸:50MIL功率(Pd):小功率设备封装:DB-4工作温度:-55°C~150°C类型:插件、小方桥、小电流DB107描述:ASEMI品牌DB107是采用工......
  • SOLIDWORKS插件SolidKits.BOMs工具之属性修改
    SOLIDWORKS模型的属性信息可以写在自定义属性中,也可以写在配置特定属性中,这些我们在制作SOLIDWORKS模板的时候就已经定义好了,如果随着企业的发展,属性名做了调整,就可以使用SOLIDWORKS属性修改插件-SolidKits.BOMs工具来批量完成属性名的修改,比如将代号改为图号,将备注属性删除等操作......
  • K8s 网关选型血泪史
    Sealos公有云几乎打爆了市面上所有主流的开源网关,本文可以给大家很好的避坑,在网关选型方面做一些参考。SealosCloud的复杂场景Sealos公有云上线以来,用户呈爆发式增长,目前总共注册用户8.7w,每个用户都去创建应用,每个应用都需要有自己的访问入口,就导致整个集群路由条目非常巨......
  • Asp .Net Core 系列:集成 Ocelot+Nacos+Swagger+Cors实现网关、服务注册、服务发现
    目录简介什么是Ocelot?什么是Nacos?什么是Swagger?什么是Cors?Asp.NetCore集成Ocelot网关集成Nacos下游配置Nacos配置跨域(Cors)网关和微服务中配置Swagger效果简介什么是Ocelot?Ocelot是一个开源的ASP.NETCore微服务网关,它提供了API网关所需的所有功能,如路由、......
  • 精品IDEA插件推荐:Apipost-Helper
    Apipost-Helper是由Apipost推出的IDEA插件,写完接口可以进行快速调试,且支持搜索接口、根据method跳转接口,还支持生成标准的API文档,注意:这些操作都可以在代码编辑器内独立完成,非常好用!这里给大家介绍一下Apipost-Helper的安装和使用安装在IDEA编辑器插件中心输入Apipost搜索安装:......
  • vue2中使用v-selectpage插件 搜索并分页
    <v-selectpagedata="/api/intrusionevent/lists"v-model="temp.event_id"key-field="id"show-field="description"search-field="de......
  • springboot配置分页插件pageHelper和数据库方言的几种方式
    方式一:启动类配置分页插件(Application.java)1/**2*pageHelper分页插件3*/4@Bean5publicPageHelperByMyselfpageHelper(){6PageHelperByMyselfpageHelper=newPageHelperByMyself();7Propertiesproperties=newPr......