使用Keycloak保护Golang REST API的安全
使用Keycloak保护Golang REST API的安全 我正在使用Golang开发REST API服务器,并希望通过Keycloak来保护所有API。有人能建议如何在Golang API服务器上实现单点登录吗?
3 回复
这个方法对我很有效……非常感谢……
更多关于使用Keycloak保护Golang REST API的安全的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Golang中集成Keycloak来保护REST API并实现单点登录(SSO),可以使用gocloak库。以下是一个完整的实现示例:
首先安装依赖:
go get github.com/Nerzal/gocloak/v13
1. 配置Keycloak客户端 在Keycloak管理控制台创建一个客户端,设置:
- 访问类型:confidential
- 有效重定向URI:你的API回调地址
- 启用客户端认证
2. Golang中间件实现
package main
import (
"context"
"fmt"
"log"
"net/http"
"strings"
"github.com/Nerzal/gocloak/v13"
"github.com/gin-gonic/gin"
)
type KeycloakConfig struct {
Realm string
ClientID string
ClientSecret string
Hostname string
}
var (
keycloakClient gocloak.GoCloak
config = KeycloakConfig{
Realm: "your-realm",
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
Hostname: "http://localhost:8080",
}
)
func init() {
keycloakClient = gocloak.NewClient("http://localhost:8080/auth")
}
// Keycloak中间件 - 验证访问令牌
func KeycloakMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
c.Abort()
return
}
token := strings.TrimPrefix(authHeader, "Bearer ")
if token == authHeader {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Bearer token required"})
c.Abort()
return
}
// 验证令牌
ctx := context.Background()
_, err := keycloakClient.IntrospectToken(ctx, token, config.ClientID, config.ClientSecret, config.Realm)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
c.Abort()
return
}
c.Next()
}
}
// 获取用户信息
func getUserInfo(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
token := strings.TrimPrefix(authHeader, "Bearer ")
ctx := context.Background()
userInfo, err := keycloakClient.GetUserInfo(ctx, token, config.Realm)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"sub": userInfo.Sub,
"email": userInfo.Email,
"preferred_username": userInfo.PreferredUsername,
})
}
// 登录处理
func loginHandler(c *gin.Context) {
ctx := context.Background()
token, err := keycloakClient.Login(ctx, config.ClientID, config.ClientSecret, config.Realm, "username", "password")
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Login failed"})
return
}
c.JSON(http.StatusOK, gin.H{
"access_token": token.AccessToken,
"refresh_token": token.RefreshToken,
"expires_in": token.ExpiresIn,
})
}
func main() {
r := gin.Default()
// 公开路由
r.POST("/login", loginHandler)
// 受保护的路由组
protected := r.Group("/api")
protected.Use(KeycloakMiddleware())
{
protected.GET("/userinfo", getUserInfo)
protected.GET("/protected", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "This is a protected endpoint"})
})
}
log.Fatal(r.Run(":8081"))
}
3. 使用示例
获取访问令牌:
curl -X POST http://localhost:8081/login \
-H "Content-Type: application/json" \
-d '{"username":"your-username","password":"your-password"}'
访问受保护的API:
curl -X GET http://localhost:8081/api/protected \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
4. 刷新令牌示例
func refreshTokenHandler(c *gin.Context) {
var req struct {
RefreshToken string `json:"refresh_token"`
}
if err := c.BindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
ctx := context.Background()
token, err := keycloakClient.RefreshToken(ctx, req.RefreshToken, config.ClientID, config.ClientSecret, config.Realm)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token refresh failed"})
return
}
c.JSON(http.StatusOK, gin.H{
"access_token": token.AccessToken,
"refresh_token": token.RefreshToken,
})
}
这个实现提供了完整的Keycloak集成,包括令牌验证、用户信息获取和令牌刷新功能。所有受保护的API端点都会自动验证Bearer令牌的有效性。

