Easy JWT implementation by Casbin library

Posted on Wed 07 May 2025 in Journal

Abstract IAM related protocols
Authors Walter Fan
 Category    learning note  
Status v1.0
Updated 2025-05-07
License CC-BY-NC-ND 4.0

Implement JWT authentication by Casbin in Go

使用 Go 编写一个简单的 HTTP 服务器,并通过 JWT(JSON Web Token)进行身份验证。此外,我们还将使用 Casbin 进行基于角色的访问控制(RBAC),以实现细粒度的权限管理。

源码地址:https://github.com/walterfan/kata-go/tree/master/kata/auth


初始化项目

首先,初始化 Go 模块并安装所需的依赖:

go mod init github.com/walterfan/kata-auth

go get github.com/gin-gonic/gin
go get github.com/golang-jwt/jwt/v5
go get github.com/casbin/casbin/v2

启动服务器

运行以下命令启动服务:

go run main.go

默认情况下,服务将在 http://localhost:8080 上运行。


测试接口

获取 JWT Token

发送 POST 请求获取 JWT Token:

export TOKEN=$(curl -s -X POST http://localhost:8080/token \
  -H "Content-Type: application/json" \
  -d '{"username":"test","password":"pass"}' | jq -r '.token')
echo $TOKEN

访问受保护的资源

  • 成功访问 /admin 接口(返回 200):
curl -v -H "Authorization: Bearer $TOKEN" http://localhost:8080/admin
  • 被拒绝访问 /user 接口(返回 403):
curl -v -H "Authorization: Bearer $TOKEN" http://localhost:8080/user

实现原理

JWT 认证中间件

我们使用 github.com/golang-jwt/jwt/v5 库来解析和验证 JWT Token。以下是 JWT 中间件的核心实现:

func JWTAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Missing token"})
            c.Abort()
            return
        }

        parts := strings.Split(authHeader, "Bearer ")
        if len(parts) != 2 {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token format"})
            c.Abort()
            return
        }

        tokenStr := parts[1]
        token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
            return []byte(jwtSecret), nil
        })
        if err != nil || !token.Valid {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
            c.Abort()
            return
        }

        claims, ok := token.Claims.(jwt.MapClaims)
        if !ok {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid claims"})
            c.Abort()
            return
        }

        c.Set("userID", claims["user_id"])
        c.Set("role", claims["role"])
        c.Next()
    }
}

该中间件会拦截所有请求,检查请求头中的 Authorization 字段是否包含有效的 JWT Token。如果 Token 有效,则从中提取用户 ID 和角色信息,供后续处理逻辑使用。


基于 Casbin 的 RBAC 权限控制

Casbin 是一个强大的开源访问控制库,支持多种编程语言(如 Go、Java、Python 等)。它提供了灵活的权限管理功能,可以实现常见的访问控制模型,例如:

  1. ACL(Access Control List):基于资源和用户的直接授权。
  2. RBAC(Role-Based Access Control):通过角色来分配权限。
  3. ABAC(Attribute-Based Access Control):基于属性的动态访问控制。
配置文件说明

Casbin 使用 .conf 文件定义访问控制模型,策略则存储在 .csv 文件中。

model.conf
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
  • request_definition 定义了访问请求的结构:
  • sub(主体):通常是用户或角色。
  • obj(对象):被访问的资源(如 /admin, /user)。
  • act(操作):对资源执行的操作(如 GET, POST)。

  • policy_definition 定义了权限规则的结构:

  • sub:有权执行操作的主体(用户或角色)。
  • obj:可访问的资源。
  • act:允许的操作。

  • policy_effect 定义了策略的效果:

  • 只要有一条策略允许该请求(即 p.eft == allow),整个请求就视为允许。

  • matchers 定义了如何将请求与策略进行匹配:

  • 只有当请求中的 sub, obj, act 都与某条策略完全匹配时,才认为该策略适用于当前请求。
policy.csv
p, admin, /admin, GET
p, user, /user, GET

表示:

  • admin 角色可以访问 /admin 接口的 GET 请求。
  • user 角色可以访问 /user 接口的 GET 请求。

如需进一步扩展功能,可以考虑以下方向:

  • 支持刷新 Token;
  • 添加数据库支持,用于持久化用户信息和权限配置;
  • 支持多租户架构;
  • 集成 OpenID Connect 或 OAuth 2.0 协议进行联合认证。

参考资料

IAM(Identity and Access Management)相关协议

IAM related protocols

Protocol Purpose Abstract Summary Spec/RFC
LDAP (Lightweight Directory Access Protocol) Centralized directory-based authentication and user lookup A protocol to query and modify identity info stored in a directory (like Active Directory). Common in enterprise systems. RFC 4510
Kerberos Network authentication using tickets A time-based ticketing system where users authenticate once and receive time-limited tickets for accessing resources securely. RFC 4120
RADIUS (Remote Authentication Dial-In User Service) Centralized authentication for network access Used for authenticating users to network devices (e.g., VPNs, Wi-Fi). Provides authentication, authorization, and accounting (AAA). RFC 2865, RFC 2866
TACACS+ Cisco-focused AAA protocol Similar to RADIUS, but separates the AAA components more clearly and encrypts the entire payload. No official RFC (Cisco proprietary); Cisco TACACS+ Documentation
SAML 2.0 (Security Assertion Markup Language) Federated SSO for enterprise applications XML-based protocol used for browser-based SSO, mainly between identity providers (IdPs) and service providers (SPs). OASIS SAML 2.0 Specification
OAuth 2.0 Authorization delegation (not authentication) A token-based authorization protocol allowing third-party apps to access user data without seeing the user’s credentials. RFC 6749
OpenID Connect (OIDC) Identity layer on top of OAuth 2.0 for SSO and identity federation Adds login and user info features to OAuth 2.0 using JSON Web Tokens (JWT). Ideal for modern web/mobile SSO. OpenID Connect Core 1.0
SCIM (System for Cross-domain Identity Management) Automating user provisioning/deprovisioning Standard for managing user identities between systems (e.g., auto-creating accounts when users join an org). RFC 7641, RFC 7642, RFC 7643, RFC 7644
JWT (JSON Web Token) Compact, self-contained format for identity/auth info Often used with OAuth/OIDC to carry identity claims. Not a protocol itself, but a building block for them. RFC 7519
WS-Federation SSO and identity federation (Microsoft-centric) XML-based protocol used in Microsoft environments to federate identity between systems. Precursor to OIDC in some orgs. WS-Federation Specification
SPIFFE (Secure Production Identity Framework for Everyone) Provides standardized identities for services in dynamic, distributed environments SPIFFE defines a way to issue cryptographically verifiable identities (SPIFFE IDs) to workloads without requiring secrets like passwords or API keys. It enables zero-trust, mutual TLS (mTLS), and secure service-to-service communication. SPIFFE ID Specification
SPIRE (SPIFFE Runtime Environment) A reference implementation of SPIFFE SPIRE issues, rotates, and manages SPIFFE identities on a running system. It provides workload attestation, certificate issuance, and integrates with K8s, VMs, and cloud providers. Think of SPIRE as the "engine" that brings SPIFFE to life. SPIRE Documentation
x509-SVID (SPIFFE Verifiable Identity Document) Standard format for SPIFFE identity An X.509 certificate format used in SPIFFE to represent a workload’s identity. Contains the SPIFFE ID in a SAN (Subject Alternative Name) field. x509-SVID Specification
JWT-SVID Token format for SPIFFE identity A JWT-based alternative to x509-SVID, useful when TLS is not feasible. Enables secure identity propagation between services. JWT-SVID Specification