Go?+?Gin實現(xiàn)雙Token管理員登錄的示例代碼
一、為什么要做「雙 Token」
傳統(tǒng)單 Token(JWT)架構下,“續(xù)簽”與“強制失效” 是一對矛盾:
單 Token 痛點 | 雙 Token 解法 |
---|---|
Token 過期后需重新登錄,體驗差 | Access-Token 失效后,用 Refresh-Token 無感刷新 |
Token 一旦泄露,在有效期內(nèi)無法撤銷 | Refresh-Token 存 Redis,可一鍵踢人 |
續(xù)簽邏輯侵入業(yè)務代碼 | 續(xù)簽、校驗、踢人全部封裝在 Auth 中間件 |
本文將用 Gin + GORM + Redis 帶你落地一套生產(chǎn)可用的「管理員雙 Token」登錄體系。
二、整體架構
┌────────────┐ ┌──────────────┐ ┌──────────┐ │ Web │──────?│ Handler │──────?│ Logic │ └────────────┘ └──────────────┘ └──────────┘ ▲ ▲ │ │ │ ▼ │ ┌──────────────┐ ┌──────────┐ │ │ Response │ │ Repo │ └────────────┘ Helper │ └──────────┘ │ Redis / MySQL
- Handler 負責參數(shù)校驗、鑒權前置檢查
- Logic 處理核心業(yè)務:登錄、刷新、退出、獲取用戶信息
- Repo 封裝數(shù)據(jù)訪問:MySQL 查管理員、Redis 存 Refresh-Token
- Response 統(tǒng)一封裝返回格式,避免樣板代碼
三、核心代碼走讀
3.1 路由層(Handler)
// NewAdminLoginHandler 管理員登錄 func NewAdminLoginHandler() gin.HandlerFunc { return func(c *gin.Context) { var req requests.AdminLoginReq if err := c.ShouldBindJSON(&req); err != nil { response.Fail(c, "參數(shù)不合法") return } repo := repositories.NewAdminLoginRepository(globals.DB, globals.RDB) logic := logics.NewAdminLoginLogic(repo) resp, err := logic.Login(c.Request.Context(), &req) if err != nil { response.Fail(c, fmt.Sprintf("登錄失敗: %v", err)) return } response.OK(c, "登錄成功", resp) } }
- 只做「綁定參數(shù) + 調(diào)用邏輯 + 包裝返回」
- 不摻雜任何業(yè)務判斷,保持 單一職責
3.2 業(yè)務層(Logic)
3.2.1 登錄
func (l *AdminLoginLogic) Login(ctx context.Context, req *requests.AdminLoginReq) (*responses.AdminLoginResp, error) { admin, err := l.findAdminByAccount(ctx, req.Account) if err != nil || admin == nil { return nil, errors.New("賬號不存在") } if admin.Status != 1 { return nil, errors.New("賬號已被禁用") } if !usersignutil.CheckPasswordHash(req.Password, admin.Password) { return nil, errors.New("密碼錯誤") } // 更新最后登錄時間 _ = l.adminRepo.UpdateLastLoginTime(ctx, admin.ID) // 生成雙 Token return l.generateTokensAndBuildResponse(ctx, admin) }
3.2.2 生成雙 Token
func (l *AdminLoginLogic) generateTokensAndBuildResponse( ctx context.Context, admin *models.Admin, ) (*responses.AdminLoginResp, error) { accessToken, _ := usersignutil.GenerateAccessToken(admin.ID) refreshToken, _ := usersignutil.GenerateRefreshToken(admin.ID) // 保存 Refresh-Token 到 Redis,設置過期時間 err := l.adminRepo.SaveRefreshToken( ctx, admin.ID, refreshToken, globals.AppConfig.JWT.RefreshTokenExpiry, ) if err != nil { // 記錄日志但不阻斷登錄 globals.Log.Error("SaveRefreshToken err:", err) } resp := (&responses.AdminLoginResp{}).ToResponse(admin) resp.AccessToken = accessToken resp.RefreshToken = refreshToken return resp, nil }
3.2.3 獲取管理員信息
func (l *AdminLoginLogic) GetAdminInfo(ctx context.Context, adminID uint) (*responses.AdminInfoResp, error) { admin, err := l.adminRepo.FindByIDWithDetails(ctx, adminID) if err != nil || admin == nil { return nil, errors.New("管理員不存在") } if admin.Status != 1 { return nil, errors.New("賬號已被禁用") } return (&responses.AdminInfoResp{}).ToResponse(admin), nil }
3.2.4 登出
func (l *AdminLoginLogic) Logout(ctx context.Context, adminID uint) error { return l.adminRepo.DeleteRefreshToken(ctx, adminID) }
3.3 數(shù)據(jù)訪問層(Repo)
僅展示關鍵函數(shù),完整代碼已在文章開頭給出。
FindByPhone / FindByAccountID / FindByIDWithDetails
利用 GORM 的Preload
一把連表查,減少 N+1SaveRefreshToken / DeleteRefreshToken
使用admin:refresh_token:{id}
作為 Redis Key,天然支持「單設備登錄」或「多端互踢」
四、如何無感刷新 Access-Token
前端收到 401 Unauthorized
后,攜帶 Refresh-Token 調(diào) /admin/refresh
:
// 偽代碼(Handler 略) refreshToken := c.GetHeader("X-Refresh-Token") adminID, err := usersignutil.ParseRefreshToken(refreshToken) if err != nil { /* 無效 Refresh-Token */ } // 與 Redis 比對 saved, _ := repo.GetRefreshToken(ctx, adminID) if saved != refreshToken { /* 已被踢出 */ } // 重新頒發(fā) newAccess, _ := usersignutil.GenerateAccessToken(adminID)
五、安全細節(jié)
細節(jié) | 實現(xiàn) |
---|---|
密碼加密 | bcrypt 哈希,不可逆 |
Token 簽名 | 使用獨立 jwtSecret ,區(qū)分 Access/Refresh |
Refresh-Token 存儲 | Redis + TTL,支持熱踢人 |
SQL 注入 | GORM 占位符自動防注入 |
并發(fā)登錄 | Redis Key 覆蓋即可實現(xiàn)「后者踢前者」 |
六、總結
本文用 200 行核心代碼展示了:
- 分層架構:Handler → Logic → Repo
- 雙 Token:Access-Token(短)+ Refresh-Token(長)
- 統(tǒng)一響應:封裝
response.OK / Fail
消除樣板 - 安全退出:Redis 刪除 Refresh-Token 即踢人
到此這篇關于Go + Gin實現(xiàn)雙Token管理員登錄的示例代碼的文章就介紹到這了,更多相關Go Gin 雙Token管理員登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
go語言規(guī)范RESTful?API業(yè)務錯誤處理
這篇文章主要為大家介紹了go語言規(guī)范RESTful?API業(yè)務錯誤處理方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03