From 1976baa164a7f314dd9d3657af12b06439a0a274 Mon Sep 17 00:00:00 2001 From: luorijun Date: Wed, 30 Apr 2025 16:39:46 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BC=9A=E8=AF=9D=E8=BF=87?= =?UTF-8?q?=E6=9C=9F=E6=97=B6=E9=97=B4=E7=9A=84=E7=8E=AF=E5=A2=83=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E9=85=8D=E7=BD=AE=EF=BC=9B=E6=92=A4=E9=94=80=E4=BB=A4?= =?UTF-8?q?=E7=89=8C=E6=8E=A5=E5=8F=A3=E6=9D=83=E9=99=90=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=94=A8=E6=88=B7=E4=BB=A4=E7=89=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 ++++++------- pkg/env/env.go | 30 +++++++++++++++++ web/handlers/auth.go | 2 +- web/services/session.go | 75 ++++++++++++----------------------------- 4 files changed, 64 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index af24050..6a6f161 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,23 @@ ## todo -- 撤销令牌需要改成用户权限,只有本人可以撤销 -- 移动端适配 -- 扩展 device 权限验证方式,提供一种方法区分内部和外部服务 -- 检查数据库枚举字段,0 值只作为空值使用 -- 更新数据库填充 +- 微信支付 - 错误处理类型转换失败问题 +- 用对称加密处理密钥 +- 检查数据库枚举字段,0 值只作为空值使用 +- 移动端适配 - channel 接口 - 重新梳理逻辑流程,简化循环 - 端口分配时加锁 -- 用对称加密处理密钥 -- 环境变量配置默认会话配置 -- 微信支付 -- 支付回调处理 -- 页面 账户总览 -- 保存 session 到数据库 - 中间件 Limiter -- 中间件 Compress +- 页面 账户总览 - 页面 提取记录 - 页面 使用记录 + +### 下阶段 + +- 支付回调处理 +- 保存 session 到数据库 +- 扩展 device 权限验证方式,提供一种方法区分内部和外部服务 - 废弃 password 授权模式,迁移到 authorization code 授权模式 - oauth token 验证授权范围 - 实现白银节点的 warp 服务,用来去重与端口分配,保证测试与生产环境不会产生端口竞争 @@ -27,6 +26,7 @@ - debug:白银节点提供一些工具接口,方便快速操作 - 批量下线端口 - 统一使用 validator 进行参数验证 +- 格式化控制台输出 ### 长期 diff --git a/pkg/env/env.go b/pkg/env/env.go index 6ed61bc..8365c66 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -24,6 +24,35 @@ func loadApp() { // endregion +// region auth + +var ( + SessionAccessExpire = 60 * 60 * 2 // 2小时 + SessionRefreshExpire = 60 * 60 * 24 * 7 // 7天 +) + +func loadAuth() { + _SessionAccessExpire := os.Getenv("SESSION_ACCESS_EXPIRE") + if _SessionAccessExpire != "" { + value, err := strconv.Atoi(_SessionAccessExpire) + if err != nil { + panic("环境变量 SESSION_ACCESS_EXPIRE 的值不是数字") + } + SessionAccessExpire = value + } + + _SessionRefreshExpire := os.Getenv("SESSION_REFRESH_EXPIRE") + if _SessionRefreshExpire != "" { + value, err := strconv.Atoi(_SessionRefreshExpire) + if err != nil { + panic("环境变量 SESSION_REFRESH_EXPIRE 的值不是数字") + } + SessionRefreshExpire = value + } +} + +// endregion + // region db var ( @@ -359,6 +388,7 @@ func Init() { } loadApp() + loadAuth() loadDb() loadRedis() loadLog() diff --git a/web/handlers/auth.go b/web/handlers/auth.go index 572572c..135dbb4 100644 --- a/web/handlers/auth.go +++ b/web/handlers/auth.go @@ -268,7 +268,7 @@ type RevokeReq struct { } func Revoke(c *fiber.Ctx) error { - _, err := auth.Protect(c, []s.PayloadType{s.PayloadClientConfidential}, []string{}) + _, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{}) if err != nil { // 用户未登录 return nil diff --git a/web/services/session.go b/web/services/session.go index 3c6bda0..ee98e51 100644 --- a/web/services/session.go +++ b/web/services/session.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "platform/pkg/env" "platform/pkg/rds" "time" @@ -20,9 +21,9 @@ type SessionServiceInter interface { // Find 通过访问令牌获取会话信息 Find(ctx context.Context, token string) (*AuthContext, error) // Create 创建一个新的会话 - Create(ctx context.Context, auth AuthContext, config ...SessionConfig) (*TokenDetails, error) + Create(ctx context.Context, auth AuthContext) (*TokenDetails, error) // Refresh 刷新一个会话 - Refresh(ctx context.Context, refreshToken string, config ...SessionConfig) (*TokenDetails, error) + Refresh(ctx context.Context, refreshToken string) (*TokenDetails, error) // Remove 删除会话 Remove(ctx context.Context, accessToken, refreshToken string) error } @@ -61,12 +62,8 @@ func (s *sessionService) Find(ctx context.Context, token string) (*AuthContext, } // Create 创建一个新的会话 -func (s *sessionService) Create(ctx context.Context, auth AuthContext, config ...SessionConfig) (*TokenDetails, error) { - // 解析可选配置 - cfg := DefaultSessionConfig - if len(config) > 0 { - cfg = mergeConfig(DefaultSessionConfig, config[0]) - } +func (s *sessionService) Create(ctx context.Context, auth AuthContext) (*TokenDetails, error) { + var now = time.Now() // 生成令牌组 accessToken := genToken() @@ -88,9 +85,12 @@ func (s *sessionService) Create(ctx context.Context, auth AuthContext, config .. } // 事务保存数据到 Redis + var accessExpire = time.Duration(env.SessionAccessExpire) * time.Second + var refreshExpire = time.Duration(env.SessionRefreshExpire) * time.Second + pipe := rds.Client.TxPipeline() - pipe.Set(ctx, accessKey(accessToken), authData, cfg.AccessTokenDuration) - pipe.Set(ctx, refreshKey(refreshToken), refreshData, cfg.RefreshTokenDuration) + pipe.Set(ctx, accessKey(accessToken), authData, accessExpire) + pipe.Set(ctx, refreshKey(refreshToken), refreshData, refreshExpire) _, err = pipe.Exec(ctx) if err != nil { return nil, err @@ -98,20 +98,16 @@ func (s *sessionService) Create(ctx context.Context, auth AuthContext, config .. return &TokenDetails{ AccessToken: accessToken, - AccessTokenExpires: time.Now().Add(cfg.AccessTokenDuration), + AccessTokenExpires: now.Add(accessExpire), RefreshToken: refreshToken, - RefreshTokenExpires: time.Now().Add(cfg.RefreshTokenDuration), + RefreshTokenExpires: now.Add(refreshExpire), Auth: auth, }, nil } // Refresh 刷新一个会话 -func (s *sessionService) Refresh(ctx context.Context, refreshToken string, config ...SessionConfig) (*TokenDetails, error) { - // 解析可选配置 - cfg := DefaultSessionConfig - if len(config) > 0 { - cfg = mergeConfig(DefaultSessionConfig, config[0]) - } +func (s *sessionService) Refresh(ctx context.Context, refreshToken string) (*TokenDetails, error) { + var now = time.Now() rKey := refreshKey(refreshToken) var tokenDetails *TokenDetails @@ -153,8 +149,11 @@ func (s *sessionService) Refresh(ctx context.Context, refreshToken string, confi pipeline := tx.Pipeline() // 保存新的令牌 - pipeline.Set(ctx, accessKey(newAccessToken), authData, cfg.AccessTokenDuration) - pipeline.Set(ctx, refreshKey(newRefreshToken), newRefreshData, cfg.RefreshTokenDuration) + var accessExpire = time.Duration(env.SessionAccessExpire) * time.Second + var refreshExpire = time.Duration(env.SessionRefreshExpire) * time.Second + + pipeline.Set(ctx, accessKey(newAccessToken), authData, accessExpire) + pipeline.Set(ctx, refreshKey(newRefreshToken), newRefreshData, refreshExpire) // 删除旧的令牌 pipeline.Del(ctx, accessKey(refreshData.AccessToken)) @@ -168,8 +167,8 @@ func (s *sessionService) Refresh(ctx context.Context, refreshToken string, confi tokenDetails = &TokenDetails{ AccessToken: newAccessToken, RefreshToken: newRefreshToken, - AccessTokenExpires: time.Now().Add(cfg.AccessTokenDuration), - RefreshTokenExpires: time.Now().Add(cfg.RefreshTokenDuration), + AccessTokenExpires: now.Add(accessExpire), + RefreshTokenExpires: now.Add(refreshExpire), Auth: refreshData.AuthContext, } return nil @@ -204,38 +203,6 @@ func refreshKey(token string) string { // endregion -// region SessionConfig - -// SessionConfig 定义会话管理的配置选项 -type SessionConfig struct { - // 令牌配置 - AccessTokenDuration time.Duration - RefreshTokenDuration time.Duration -} - -// DefaultSessionConfig 默认会话配置 -var DefaultSessionConfig = SessionConfig{ - AccessTokenDuration: 2 * time.Hour, - RefreshTokenDuration: 7 * 24 * time.Hour, -} - -// 合并配置,保留非零值 -func mergeConfig(defaultCfg SessionConfig, customCfg SessionConfig) SessionConfig { - result := defaultCfg - - if customCfg.AccessTokenDuration != 0 { - result.AccessTokenDuration = customCfg.AccessTokenDuration - } - - if customCfg.RefreshTokenDuration != 0 { - result.RefreshTokenDuration = customCfg.RefreshTokenDuration - } - - return result -} - -// endregion - // region AuthContext // AuthContext 定义认证信息