GoFrame框架中GToken的使用指南
简介
GToken 是一个为 GoFrame 应用程序提供的轻量级认证中间件,提供基于令牌的身份验证功能。本指南将详细介绍如何在 GoFrame 应用程序中实现 GToken,包括基本设置、配置和常见使用模式。
基础设置
安装
首先,确保安装必要的依赖:
go get github.com/goflyfox/gtoken
go get github.com/gogf/gf/v2
项目结构
使用 GToken 的典型 GoFrame 应用程序结构如下:
├── main.go
├── config/
│ └── config.yaml
└── router/
└── router.go
核心概念
1. GToken 配置
GToken 提供了丰富的配置选项:
gfToken := >oken.GfToken{
ServerName: "your-server-name", // 服务名称
LoginPath: "/login", // 登录路径
LoginBeforeFunc: loginFunc, // 登录验证方法
LogoutPath: "/user/logout", // 登出路径
AuthPaths: g.SliceStr{"/user", "/system"}, // 需要验证的路径前缀
AuthExcludePaths: g.SliceStr{"/user/info"}, // 无需验证路径
MultiLogin: true, // 是否允许多点登录
GlobalMiddleware: true, // 是否开启全局中间件模式
CacheMode: 1, // 缓存模式
CacheKey: "gtoken:", // 缓存前缀
Timeout: 10 * 1000, // 超时时间(毫秒)
MaxRefresh: 5 * 1000, // 最大刷新时间(毫秒)
TokenDelimiter: "##", // Token分隔符
EncryptKey: []byte("your-key"), // 加密key
AuthFailMsg: "登录失败", // 认证失败提示
}
关键参数说明:
ServerName:服务器唯一标识LoginPath:登录接口路径LoginBeforeFunc:登录处理函数LogoutPath:登出接口路径AuthPaths:需要验证的路径前缀列表,支持前缀匹配AuthExcludePaths:不需要验证的路径列表,优先级高于 AuthPathsMultiLogin:是否支持多点登录GlobalMiddleware:是否开启全局中间件模式CacheMode:缓存模式,支持内存和Redis等多种方式CacheKey:缓存键前缀Timeout:Token超时时间MaxRefresh:Token最大刷新时间TokenDelimiter:Token分隔符EncryptKey:加密密钥AuthFailMsg:认证失败提示信息
2. 认证流程
登录处理器
登录处理函数是认证过程的核心:
func Login(r *ghttp.Request) (string, interface{}) {
username := r.Get("username").String()
passwd := r.Get("passwd").String()
if username == "" || passwd == "" {
r.Response.WriteJson(gtoken.Fail("账号或密码错误"))
r.ExitAll()
}
// 返回用户唯一标识和用户数据
return username, "userData"
}
路由配置
使用 GToken 中间件配置路由:
func initRouter(s *ghttp.Server) {
// 公开路由
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(CORS)
group.ALL("/hello", publicHandler)
})
// 需要认证的路由
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(CORS)
gfToken.Middleware(ctx, group)
group.ALL("/user/data", protectedHandler)
})
}
配置管理
1. 从配置文件读取
GToken 支持从配置文件中读取配置,这是一个推荐的做法:
func CfgGet(ctx context.Context, name string) *gvar.Var {
gVar, _ := g.Config().Get(ctx, name)
return gVar
}
// 使用配置文件初始化 GToken
gfToken = >oken.GfToken{
ServerName: TestServerName,
CacheMode: CfgGet(ctx, "gToken.CacheMode").Int8(),
CacheKey: CfgGet(ctx, "gToken.CacheKey").String(),
Timeout: CfgGet(ctx, "gToken.Timeout").Int(),
MaxRefresh: CfgGet(ctx, "gToken.MaxRefresh").Int(),
TokenDelimiter: CfgGet(ctx, "gToken.TokenDelimiter").String(),
EncryptKey: CfgGet(ctx, "gToken.EncryptKey").Bytes(),
AuthFailMsg: CfgGet(ctx, "gToken.AuthFailMsg").String(),
MultiLogin: CfgGet(ctx, "gToken.MultiLogin").Bool(),
// ... 其他配置
}
2. 配置文件示例
config.yaml 文件示例:
gToken:
CacheMode: 1
CacheKey: "gtoken:"
Timeout: 10800000
MaxRefresh: 5400000
TokenDelimiter: "#"
EncryptKey: "49c54195e750b04e74a8429b17896586"
AuthFailMsg: "登录失败,请重新登录"
MultiLogin: true
3. 全局中间件模式
GToken 支持全局中间件模式,通过设置 GlobalMiddleware: true 启用:
gfToken = >oken.GfToken{
// ... 其他配置
AuthPaths: g.SliceStr{"/user", "/system"}, // 拦截的路径前缀
GlobalMiddleware: true, // 开启全局中间件
}
// 启动 GToken
err := gfToken.Start()
if err != nil {
panic(err)
}
在全局中间件模式下:
- 所有匹配 AuthPaths 前缀的路径都会被拦截
- AuthExcludePaths 中的路径会被排除在拦截之外
- 无需在每个路由组中手动添加中间件
缓存模式与存储
1. 缓存模式选择
GToken 支持多种缓存模式:
const (
CacheModeCache = 1 // 使用缓存模式
CacheModeRedis = 2 // 使用Redis模式
)
缓存模式配置示例:
// 内存缓存模式
gfToken := >oken.GfToken{
CacheMode: 1,
CacheKey: "gtoken:",
}
// Redis模式
gfToken := >oken.GfToken{
CacheMode: 2,
CacheKey: "gtoken:",
}
2. Redis配置
如果使用Redis模式,需要在配置文件中添加Redis配置:
redis:
default:
address: 127.0.0.1:6379
db: 1
pass: ""
maxIdle: 10
maxActive: 100
idleTimeout: 600
路由管理与分组
1. 多级路由分组
GToken 支持灵活的路由分组管理:
func initRouter(s *ghttp.Server) {
// 公共路由组
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(CORS)
// 无需认证的接口
group.ALL("/hello", func(r *ghttp.Request) {
r.Response.WriteJson(gtoken.Succ("hello"))
})
})
// 用户相关路由组
s.Group("/user", func(group *ghttp.RouterGroup) {
// 用户数据接口
group.ALL("/data", func(r *ghttp.Request) {
tokenData := gfToken.GetTokenData(r)
r.Response.WriteJson(tokenData)
})
// 用户信息接口
group.ALL("/info", func(r *ghttp.Request) {
r.Response.WriteJson(gtoken.Succ("user info"))
})
})
// 系统管理路由组
s.Group("/system", func(group *ghttp.RouterGroup) {
// 系统数据接口
group.ALL("/data", func(r *ghttp.Request) {
r.Response.WriteJson(gfToken.GetTokenData(r).Data)
})
// 系统用户接口
group.ALL("/user", func(r *ghttp.Request) {
r.Response.WriteJson(gtoken.Succ("system user"))
})
})
}
2. 路由中间件管理
// 全局中间件设置
s.Use(ghttp.MiddlewareHandlerResponse)
s.Use(ghttp.MiddlewareCORS)
// 分组中间件设置
s.Group("/api", func(group *ghttp.RouterGroup) {
group.Middleware(
CORS,
gfToken.Middleware,
// 其他中间件...
)
})
错误处理与响应
1. 统一响应格式
为了保持API响应的一致性,建议定义统一的响应格式:
// 成功响应
r.Response.WriteJson(gtoken.Succ("操作成功", data))
// 失败响应
r.Response.WriteJson(gtoken.Fail("未授权访问"))
// 自定义状态码响应
r.Response.WriteJson(gtoken.Json(1, "消息内容", data))
2. 错误处理最佳实践
// 登录处理中的错误处理
func Login(r *ghttp.Request) (string, interface{}) {
username := r.Get("username").String()
passwd := r.Get("passwd").String()
// 参数验证
if username == "" || passwd == "" {
r.Response.WriteJson(gtoken.Fail("账号或密码不能为空"))
r.ExitAll()
}
// 用户验证
user, err := service.User.GetUserByUsername(r.Context(), username)
if err != nil {
r.Response.WriteJson(gtoken.Fail("用户验证失败"))
r.ExitAll()
}
// 密码验证
if !service.User.ComparePassword(passwd, user.Password) {
r.Response.WriteJson(gtoken.Fail("密码错误"))
r.ExitAll()
}
// 返回用户信息
return username, g.Map{
"id": user.Id,
"name": user.Name,
"role": user.Role,
}
}
// 中间件错误处理
func ErrorHandler(r *ghttp.Request) {
defer func() {
if err := recover(); err != nil {
r.Response.WriteJson(gtoken.Fail("服务器内部错误"))
r.ExitAll()
}
}()
r.Middleware.Next()
}
高级特性
1. 多令牌支持
GToken 支持维护多个认证上下文:
// 用户令牌
gfToken := >oken.GfToken{
ServerName: "user-auth",
LoginPath: "/login",
}
// 管理员令牌
gfAdminToken := >oken.GfToken{
ServerName: "admin-auth",
LoginPath: "/admin/login",
}
2. CORS 支持
实现跨域请求支持的中间件:
func CORS(r *ghttp.Request) {
r.Response.CORSDefault()
r.Middleware.Next()
}
3. 令牌数据访问与管理
获取令牌数据
// 在请求处理函数中获取令牌数据
group.ALL("/user/data", func(r *ghttp.Request) {
// 获取完整的令牌数据
tokenData := gfToken.GetTokenData(r)
// 获取令牌中的用户数据
userData := tokenData.Data
// 获取令牌中的唯一标识
identifier := tokenData.Id
r.Response.WriteJson(gtoken.Succ("success", tokenData))
})
令牌刷新机制
// 配置令牌刷新
gfToken := >oken.GfToken{
Timeout: 10 * 60 * 1000, // 令牌有效期10分钟
MaxRefresh: 30 * 60 * 1000, // 令牌最大刷新时间30分钟
}
// 在中间件中令牌会自动刷新
func tokenMiddleware(r *ghttp.Request) {
// 令牌验证和刷新逻辑已在GToken中间件中自动处理
r.Middleware.Next()
}
自定义令牌格式
// 自定义令牌分隔符
gfToken := >oken.GfToken{
TokenDelimiter: "##", // 设置令牌分隔符
EncryptKey: []byte("your-custom-key"), // 设置加密密钥
}
在受保护的路由中访问令牌数据:
group.ALL("/user/data", func(r *ghttp.Request) {
tokenData := gfToken.GetTokenData(r)
r.Response.WriteJson(tokenData)
})
最佳实践
-
错误处理
- 优雅处理认证错误
- 提供清晰的错误信息
- 实现适当的日志记录
-
安全考虑
- 生产环境使用 HTTPS
- 实现请求频率限制
- 设置合适的令牌过期时间
- 对用户输入进行安全过滤
-
配置管理
- 使用配置文件管理令牌设置
- 分离开发和生产环境配置
- 使用环境变量存储敏感数据
完整示例
以下是一个完整的 GoFrame 应用程序示例:
package main
import (
"context"
"github.com/goflyfox/gtoken/gtoken"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func main() {
ctx := context.TODO()
s := g.Server()
// 配置文件路径设置
if fileConfig, ok := g.Cfg().GetAdapter().(*gcfg.AdapterFile); ok {
fileConfig.SetPath("config")
}
// 初始化GToken
gfToken := >oken.GfToken{
ServerName: "my-app",
LoginPath: "/login",
LoginBeforeFunc: Login,
LogoutPath: "/logout",
AuthExcludePaths: g.SliceStr{"/public"},
MultiLogin: true,
}
// 注册路由
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(CORS)
gfToken.Middleware(ctx, group)
// 受保护的路由
group.ALL("/user/data", getUserData)
group.ALL("/user/info", getUserInfo)
})
s.Run()
}
// 登录处理函数
func Login(r *ghttp.Request) (string, interface{}) {
username := r.Get("username").String()
passwd := r.Get("passwd").String()
if username == "" || passwd == "" {
r.Response.WriteJson(gtoken.Fail("账号或密码错误"))
r.ExitAll()
}
return username, "1"
}
// CORS中间件
func CORS(r *ghttp.Request) {
r.Response.CORSDefault()
r.Middleware.Next()
}
总结
GToken 为 GoFrame 应用程序提供了一个强大的认证解决方案。通过本指南和提供的示例,您可以在应用程序中实现安全的身份验证,同时保持代码的清晰和可维护性。
在实际应用中,请始终考虑您的具体安全需求,并相应地调整配置。建议定期进行安全审计和更新,以确保您的认证系统保持安全。