使用ZITADEL保护Go API的完整指南

使用ZITADEL保护Go API的完整指南

zitadel ZITADEL - Identity infrastructure, simplified for you. zitadel 项目地址: https://gitcode.com/gh_mirrors/zi/zitadel

前言

在现代应用开发中,API安全是至关重要的环节。本文将详细介绍如何使用ZITADEL身份认证平台来保护Go语言编写的API服务。通过OAuth 2.0 Token Introspection机制,我们可以为API端点添加精细的访问控制。

准备工作

环境要求

在开始之前,请确保您已经具备以下条件:

  • 一个可用的ZITADEL实例
  • Go 1.16或更高版本
  • 基本的Go语言开发环境

ZITADEL控制台配置

首先需要在ZITADEL控制台完成以下配置步骤:

  1. 创建应用

    • 在控制台首页点击"创建应用"
    • 选择项目并选择"Other"作为框架类型
    • 填写应用名称并选择"API"作为应用类型
  2. 配置认证方式

    • 选择JWT作为认证方法
    • 创建新的JSON密钥并设置合适的过期时间
    • 务必下载并保存密钥文件
  3. 创建服务用户

    • 在用户管理界面切换到"服务用户"标签
    • 创建新用户并设置名称和用户名
    • 选择Bearer作为访问令牌类型
  4. 生成个人访问令牌(PAT)

    • 在用户管理的左侧面板选择"个人访问令牌"
    • 设置过期时间并生成令牌
    • 安全保存生成的令牌,后续开发将使用它

Go项目设置

添加依赖

首先需要将ZITADEL Go SDK添加到项目中:

go get -u github.com/zitadel/zitadel-go/v3

API示例代码解析

下面是一个完整的API示例,包含三个端点:

  1. 健康检查端点 (/api/healthz)

    • 公开访问,无需认证
    • 始终返回"OK"响应
  2. 任务列表端点 (/api/tasks)

    • 需要有效访问令牌
    • 返回当前任务列表
  3. 添加任务端点 (/api/add-task)

    • 需要具有"admin"角色的访问令牌
    • 允许添加新任务到列表
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"sync"
	
	"github.com/zitadel/zitadel-go/v3/pkg/authorization"
	"github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth"
	"github.com/zitadel/zitadel-go/v3/pkg/http/middleware"
	"github.com/zitadel/zitadel-go/v3/pkg/zitadel"
)

var (
	domain = flag.String("domain", "", "Your ZITADEL instance domain")
	key    = flag.String("key", "", "Path to the key.json")
	port   = flag.String("port", "8089", "Port to run the server on")
)

func main() {
	flag.Parse()
	
	// 初始化ZITADEL客户端
	client, err := zitadel.New(*domain, *key, zitadel.WithJWTProfile())
	if err != nil {
		log.Fatalf("failed to create client: %v", err)
	}
	
	// 创建授权中间件
	auth, err := authorization.New(client,
		authorization.WithOPA(authorization.DefaultOPAConfig),
	)
	if err != nil {
		log.Fatalf("failed to create auth: %v", err)
	}
	
	// 创建路由
	router := http.NewServeMux()
	
	// 健康检查端点
	router.HandleFunc("/api/healthz", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprint(w, `"OK"`)
	})
	
	// 任务存储
	var (
		tasks     = make(map[string]struct{})
		tasksLock sync.Mutex
	)
	
	// 任务列表端点
	router.Handle("/api/tasks", auth.RequireAuthorization(
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			tasksLock.Lock()
			defer tasksLock.Unlock()
			
			// 检查用户角色
			ctx := r.Context()
			claims, ok := middleware.ClaimsFromContext(ctx)
			if !ok {
				http.Error(w, "claims not found", http.StatusInternalServerError)
				return
			}
			
			// 如果是管理员,添加特殊任务
			result := make(map[string][]string)
			result["tasks"] = make([]string, 0, len(tasks)+1)
			for task := range tasks {
				result["tasks"] = append(result["tasks"], task)
			}
			
			if claims.Roles["admin"] {
				result["tasks"] = append(result["tasks"], "create a new task on /api/add-task")
			}
			
			w.Header().Set("Content-Type", "application/json")
			if err := json.NewEncoder(w).Encode(result); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}),
		oauth.Introspection(),
	))
	
	// 添加任务端点
	router.Handle("/api/add-task", auth.RequireAuthorization(
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if err := r.ParseForm(); err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			
			task := r.FormValue("task")
			if task == "" {
				http.Error(w, "task is required", http.StatusBadRequest)
				return
			}
			
			tasksLock.Lock()
			defer tasksLock.Unlock()
			tasks[task] = struct{}{}
			
			w.Header().Set("Content-Type", "application/json")
			fmt.Fprintf(w, `"task '%s' added"`, task)
		}),
		oauth.Introspection(),
		authorization.RequireRole("admin"),
	))
	
	// 启动服务器
	log.Printf("INFO server listening, press ctrl+c to stop addr=http://localhost:%s", *port)
	if err := http.ListenAndServe(":"+*port, router); err != nil {
		log.Fatalf("failed to start server: %v", err)
	}
}

运行和测试API

启动服务

使用以下命令启动API服务:

go run main.go --domain your-domain.zitadel.cloud --key ./api.json

成功启动后,您将看到日志输出:

INFO server listening, press ctrl+c to stop addr=http://localhost:8089

测试端点

  1. 测试健康检查端点
curl -i http://localhost:8089/api/healthz

预期响应:

HTTP/1.1 200 OK
"OK"
  1. 测试任务列表端点(无令牌)
curl -i http://localhost:8089/api/tasks

预期响应:

HTTP/1.1 401 Unauthorized
unauthorized: authorization header is empty
  1. 测试任务列表端点(带有效令牌)
curl -i -H "Authorization: Bearer ${token}" http://localhost:8089/api/tasks

预期响应(初始为空列表):

HTTP/1.1 200 OK
{}
  1. 测试添加任务端点(无admin角色)
curl -i -H "Authorization: Bearer ${token}" http://localhost:8089/api/add-task --data "task=My new task"

预期响应:

HTTP/1.1 403 Forbidden
permission denied: missing required role: `admin`

配置admin角色

  1. 在ZITADEL控制台中,导航到您的项目
  2. 选择"Roles"并创建名为"admin"的新角色
  3. 在"Authorization"部分,将该角色授予您的服务用户

再次测试

  1. 添加任务(带admin角色)
curl -i -H "Authorization: Bearer ${token}" http://localhost:8089/api/add-task --data "task=My new task"

预期响应:

HTTP/1.1 200 OK
"task 'My new task' added"
  1. 查看更新后的任务列表
curl -i -H "Authorization: Bearer ${token}" http://localhost:8089/api/tasks

预期响应(注意自动添加的特殊任务):

HTTP/1.1 200 OK
{"tasks":["My new task","create a new task on /api/add-task"]}

最佳实践和安全建议

  1. 密钥管理

    • 永远不要将密钥文件提交到版本控制系统
    • 考虑使用密钥管理服务来存储和访问密钥
  2. 令牌处理

    • 设置合理的令牌过期时间
    • 实现令牌刷新机制
  3. 角色设计

    • 遵循最小权限原则
    • 创建细粒度的角色定义
  4. 错误处理

    • 避免在错误响应中泄露敏感信息
    • 记录安全相关事件

通过本指南,您已经学会了如何使用ZITADEL为Go API添加强大的身份认证和授权功能。这种集成方式不仅安全可靠,而且可以轻松扩展到更复杂的权限场景。

zitadel ZITADEL - Identity infrastructure, simplified for you. zitadel 项目地址: https://gitcode.com/gh_mirrors/zi/zitadel

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昌隽艳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值