auth 模块为 darkit/gin 提供认证、授权、会话管理和令牌安全功能。它通过项目友好的 API 包装底层认证核心,支持多个层级的工作模式。
c.Auth() 保持请求级认证的可用性AuthContext 是请求作用域的门面。它从传入请求中提取令牌,委托给 Manager,并为以下操作提供紧凑的 API:
// 登录/登出
token, err := c.Auth().Login(loginID, device)
err := c.Auth().Logout()
// 登录状态检查
err := c.Auth().CheckLogin()
isLogin, _ := c.Auth().IsLogin()
// 获取登录ID
loginID, err := c.Auth().LoginID()
// 权限检查
err := c.Auth().CheckPermission(permission)
err := c.Auth().CheckAnyPermission(permissions...)
err := c.Auth().CheckAllPermissions(permissions...)
// 角色检查
err := c.Auth().CheckRole(role)
err := c.Auth().CheckAnyRole(roles...)
err := c.Auth().CheckAllRoles(roles...)
// 禁用状态检查
err := c.Auth().CheckDisable()
// 会话访问
session, err := c.Auth().GetSession()
// 刷新令牌交换
pair, err := c.Auth().Refresh(newRefreshToken)
Manager 是模块的核心运行时对象。所有认证状态最终都通过它流动,无论调用来自 AuthContext、全局助手、中间件还是 StpLogic。
// 创建管理器
mgr := auth.NewManager(storage, &cfg)
// 登录
token, err := mgr.Login(loginID, device, permissoins...)
pair, err := mgr.LoginWithRefreshToken(loginID, device)
// 登出
err := mgr.Logout(token)
err := mgr.LogoutByLoginID(loginID)
// 状态检查
isLogin := mgr.IsLogin(token)
loginID, _ := mgr.GetLoginID(token)
// 权限/角色
mgr.SetPermissions(loginID, permissions)
mgr.SetRoles(loginID, roles)
hasPerm := mgr.HasPermission(loginID, permission)
hasRole := mgr.HasRole(loginID, role)
// 会话
session, _ := mgr.GetSession(loginID)
// 禁用/启用
err := mgr.Disable(loginID)
err := mgr.Enable(loginID)
// 踢出
err := mgr.Kickout(loginID)
err := mgr.KickoutByToken(token)
// 刷新令牌
newToken, _ := mgr.RefreshAccessToken(refreshToken)
Session 是按登录身份存储在配置后端的状态:
type Session struct {
LoginID string
Device string
LoginTime time.Time
Permissions []string
Roles []string
}
type TokenInfo struct {
LoginID string
Device string
CreateTime time.Time
ActiveTime time.Time
Tag string
}
当认证是应用程序运行时的一部分时,使用 gin.WithAuth(...):
package main
import (
"time"
ginx "github.com/darkit/gin"
"github.com/darkit/gin/auth"
)
func main() {
e := ginx.New(
ginx.WithAuth(auth.AuthConfig{
Secret: "replace-me",
Expiry: 24 * time.Hour,
TokenStyle: auth.TokenStyleJWT,
}),
)
e.POST("/login", func(c *ginx.Context) {
token, err := c.Auth().Login("user-1001", "web")
if err != nil {
c.InternalError(err.Error())
return
}
c.Success(ginx.H{"token": token})
})
_ = e.Run(":8080")
}
在处理器内部,c.Auth() 返回一个由引擎管理器支持的 *auth.AuthContext:
func handleProfile(c *gin.Context) {
// 检查登录状态
if err := c.Auth().CheckLogin(); err != nil {
c.Unauthorized(err.Error())
return
}
// 获取登录ID
loginID, err := c.Auth().LoginID()
if err != nil {
c.Unauthorized(err.Error())
return
}
// 检查权限
if err := c.Auth().CheckAnyPermission("user:read", "profile:read"); err != nil {
c.Forbidden(err.Error())
return
}
c.Success(gin.H{"login_id": loginID})
}
当您想要无需持有管理器引用的进程级认证访问时,使用全局 API:
package main
import (
"fmt"
"github.com/darkit/gin/auth"
)
func main() {
// 初始化全局管理器
cfg := auth.DefaultAuthConfig()
mgr := auth.NewManager(auth.NewMemoryStorage(), &cfg)
auth.SetGlobalManager(mgr)
defer auth.CloseGlobalManager()
// 登录
token, err := auth.Login("user-1001", "web")
if err != nil {
panic(err)
}
// 检查登录状态
isLogin := auth.IsLogin(token)
fmt.Println("Is logged in:", isLogin)
// 获取登录ID
loginID, _ := auth.GetLoginID(token)
fmt.Println("Login ID:", loginID)
// 登出
auth.Logout(token)
}
如果您想在不使用全局状态的情况下使用中间件或认证逻辑:
cfg := auth.DefaultAuthConfig()
storage := auth.NewMemoryStorage()
mgr := auth.NewManager(storage, &cfg)
builder := auth.NewMiddlewareBuilder(mgr)
router.Use(builder.AuthRequired())
router.GET("/admin", builder.RoleRequired("admin"), adminHandler)
当一个进程承载多个不能共享管理器状态的认证域名时:
adminMgr := auth.NewManager(auth.NewMemoryStorage(), &cfg)
userMgr := auth.NewManager(auth.NewMemoryStorage(), &cfg)
adminLogic := auth.NewStpLogic(adminMgr)
userLogic := auth.NewStpLogic(userMgr)
adminToken, _ := adminLogic.Login("admin-1", "web")
userToken, _ := userLogic.Login("user-1", "app")
type AuthConfig struct {
// 密钥,用于 JWT 签名等
Secret string
// 令牌过期时间
Expiry time.Duration
// 令牌样式
TokenStyle TokenStyle
// 令牌名称,默认 "Authorization"
TokenName string
// 是否从 Cookie 读取令牌
AllowCookie bool
// 是否从查询参数读取令牌
AllowQuery bool
// 是否从表单读取令牌
AllowForm bool
// 是否允许并发登录
AllowConcurrent bool
// 是否共享令牌(同一设备复用)
ShareToken bool
// 最大登录数
MaxLoginCount int
// 是否自动续期
AutoRenew bool
// 续期间隔
RenewInterval time.Duration
// 最大续期 TTL
MaxRefresh time.Duration
// 存储后端
Storage Storage
// 权限加载器
PermissionLoader PermissionLoader
// 角色加载器
RoleLoader RoleLoader
}
const (
TokenStyleUUID TokenStyle = "uuid"
TokenStyleSimple TokenStyle = "simple"
TokenStyleRandom32 TokenStyle = "random32"
TokenStyleRandom64 TokenStyle = "random64"
TokenStyleRandom128 TokenStyle = "random128"
TokenStyleJWT TokenStyle = "jwt"
TokenStyleHash TokenStyle = "hash"
TokenStyleTimestamp TokenStyle = "timestamp"
TokenStyleTik TokenStyle = "tik"
)
cfg := auth.DefaultAuthConfig()
storage := auth.NewMemoryStorage()
适用于:
特性:
storage, err := auth.NewRedisStorage("redis://localhost:6379/0")
if err != nil {
panic(err)
}
cfg := auth.DefaultAuthConfig()
cfg.Storage = storage
mgr := auth.NewManager(storage, &cfg)
适用于:
特性:
// 登录时获取令牌对
pair, err := mgr.LoginWithRefreshToken("user-1001", "web")
if err != nil {
panic(err)
}
// 使用刷新令牌获取新的访问令牌
newToken, err := mgr.RefreshAccessToken(pair.RefreshToken)
if err != nil {
panic(err)
}
// 生成随机数
nonce, _ := mgr.GenerateNonce(token)
// 验证随机数(一次性使用)
ok, _ := mgr.VerifyNonce(token, nonce)
server := mgr.GetOAuth2Server()
// 客户端注册
clientID, _ := server.RegisterClient("my-app", "secret")
// 授权码
code, _ := server.GenerateCode(token, clientID, "redirect-uri")
// 令牌交换
tokenPair, _ := server.Exchange(code, clientID, "secret")
// 刷新
newPair, _ := server.Refresh(pair.RefreshToken)
// 撤销
server.Revoke(token)
// 设置权限
mgr.SetPermissions("user-1001", []string{"user:read", "user:write", "article:*"})
// 权限检查
hasRead := mgr.HasPermission("user-1001", "user:read")
hasWrite := mgr.HasPermission("user-1001", "user:write")
hasAll := mgr.HasPermission("user-1001", "user:*") // 支持通配符
权限匹配支持:
user:read*user:*article:*:edit// 设置角色
mgr.SetRoles("user-1001", []string{"admin", "editor"})
// 角色检查
isAdmin := mgr.HasRole("user-1001", "admin")
hasRole := mgr.HasRolesOr("user-1001", "admin", "editor")
allRoles := mgr.HasRolesAnd("user-1001", "admin", "editor")
| 方法 | 说明 |
|---|---|
Login(loginID, device string) (string, error) | 用户登录 |
Logout() error | 用户登出 |
CheckLogin() error | 检查登录状态 |
IsLogin() (bool, error) | 是否已登录 |
LoginID() (string, error) | 获取登录ID |
CheckPermission(permission string) error | 检查单个权限 |
CheckAnyPermission(permissions ...string) error | 检查任意权限(OR) |
CheckAllPermissions(permissions ...string) error | 检查所有权限(AND) |
CheckRole(role string) error | 检查单个角色 |
CheckAnyRole(roles ...string) error | 检查任意角色(OR) |
CheckAllRoles(roles ...string) error | 检查所有角色(AND) |
CheckDisable() error | 检查禁用状态 |
GetSession() (*Session, error) | 获取会话信息 |
Refresh(refreshToken string) (*TokenPair, error) | 刷新令牌 |
| 方法 | 说明 |
|---|---|
NewManager(storage Storage, cfg *AuthConfig) *Manager | 创建管理器 |
Login(loginID, device string, permissions ...string) (string, error) | 登录 |
LoginWithRefreshToken(loginID, device string) (*TokenPair, error) | 带刷新的登录 |
Logout(token string) error | 登出 |
LogoutByLoginID(loginID string) error | 按登录ID登出 |
IsLogin(token string) bool | 检查登录状态 |
GetLoginID(token string) (string, error) | 获取登录ID |
GetTokenInfo(token string) (*TokenInfo, error) | 获取令牌信息 |
Disable(loginID string) error | 禁用账户 |
Enable(loginID string) error | 启用账户 |
Kickout(loginID string) error | 踢出用户 |
KickoutByToken(token string) error | 按令牌踢出 |
SetPermissions(loginID string, permissions []string) | 设置权限 |
SetRoles(loginID string, roles []string) | 设置角色 |
HasPermission(loginID, permission string) bool | 检查权限 |
HasRole(loginID, role string) bool | 检查角色 |
GetSession(loginID string) (*Session, error) | 获取会话 |
RefreshAccessToken(refreshToken string) (string, error) | 刷新令牌 |
GenerateNonce(token string) (string, error) | 生成随机数 |
VerifyNonce(token, nonce string) (bool, error) | 验证随机数 |
| 方法 | 说明 |
|---|---|
AuthRequired() gin.HandlerFunc | 需要登录 |
RoleRequired(roles ...string) gin.HandlerFunc | 需要角色 |
PermRequired(permission string) gin.HandlerFunc | 需要权限 |
DisableCheck() gin.HandlerFunc | 检查禁用 |
package main
import (
"fmt"
"github.com/darkit/gin/auth"
)
func main() {
cfg := auth.DefaultAuthConfig()
mgr := auth.NewManager(auth.NewMemoryStorage(), &cfg)
token, err := mgr.Login("user-1001", "web")
if err != nil {
panic(err)
}
if !mgr.IsLogin(token) {
panic("expected token to be valid")
}
loginID, err := mgr.GetLoginID(token)
if err != nil {
panic(err)
}
fmt.Println("Login ID:", loginID)
fmt.Println("Token:", token)
}
package main
import (
"log"
"time"
ginx "github.com/darkit/gin"
"github.com/darkit/gin/auth"
"github.com/darkit/gin/middleware"
)
func main() {
cfg := auth.DefaultAuthConfig()
mgr := auth.NewManager(auth.NewMemoryStorage(), &cfg)
builder := auth.NewMiddlewareBuilder(mgr)
e := ginx.New()
r := e.Router()
r.Use(middleware.Recovery())
r.Use(middleware.Logger())
r.Use(middleware.RequestID())
// 公开接口
r.POST("/login", func(c *ginx.Context) {
token, err := c.Auth().Login("admin", "web")
if err != nil {
c.Unauthorized(err.Error())
return
}
c.Success(ginx.H{"token": token})
})
// 需要登录
authGroup := r.Group("")
authGroup.Use(builder.AuthRequired())
authGroup.GET("/profile", func(c *ginx.Context) {
loginID, _ := c.Auth().LoginID()
c.Success(ginx.H{"login_id": loginID})
})
// 需要特定角色
adminGroup := r.Group("/admin")
adminGroup.Use(builder.RoleRequired("admin"))
adminGroup.GET("/users", func(c *ginx.Context) {
c.Success(ginx.H{"users": []string{"alice", "bob"}})
})
_ = e.Run(":8080")
}
// 设置权限
mgr.SetPermissions("user-1001", []string{"user:read", "user:*"})
mgr.SetRoles("user-1001", []string{"admin"})
canRead := mgr.HasPermission("user-1001", "user:read")
canWrite := mgr.HasPermission("user-1001", "user:write")
isAdmin := mgr.HasRole("user-1001", "admin")
session, err := mgr.GetSession("user-1001")
if err != nil {
panic(err)
}
_ = session.LoginID
_ = session.Device
_ = session.LoginTime
_ = session.Permissions
_ = session.Roles
pair, err := mgr.LoginWithRefreshToken("user-1001", "web")
if err != nil {
panic(err)
}
next, err := mgr.RefreshAccessToken(pair.RefreshToken)
if err != nil {
panic(err)
}
_ = next // 新的访问令牌