OIDC 登录/绑定回调写系统日志
CI / test (push) Successful in 30s

This commit is contained in:
2026-07-14 11:00:55 +08:00
parent b4ef98a25e
commit 2fea315430
5 changed files with 41 additions and 12 deletions
+25 -2
View File
@@ -5,10 +5,11 @@ import (
"net/http"
"net/url"
"strconv"
"time"
"github.com/gin-gonic/gin"
_ "oci-portal/internal/model" // swagger 注解引用
"oci-portal/internal/model"
"oci-portal/internal/service"
)
@@ -17,6 +18,7 @@ import (
type authxHandler struct {
auth *service.AuthService
oauth *service.OAuthService
logs *service.SystemLogService
}
// ---- TOTP(JWT 组内) ----
@@ -301,10 +303,12 @@ func (h *authxHandler) bearerUser(c *gin.Context) (string, bool) {
// @Success 302 "登录 token 经 fragment 回前端,绑定回设置页"
// @Router /api/v1/auth/oauth/{provider}/callback [get]
func (h *authxHandler) oauthCallback(c *gin.Context) {
start := time.Now()
provider := c.Param("provider")
token, _, mode, err := h.oauth.HandleCallback(
token, username, mode, err := h.oauth.HandleCallback(
c.Request.Context(), provider, c.Query("state"), c.Query("code"))
if err != nil {
h.recordOauth(c, username, http.StatusUnauthorized, oauthErrText(err), start)
target := "/login"
if mode == "bind" {
target = "/settings"
@@ -312,6 +316,7 @@ func (h *authxHandler) oauthCallback(c *gin.Context) {
c.Redirect(http.StatusFound, target+"?oauthError="+url.QueryEscape(oauthErrText(err)))
return
}
h.recordOauth(c, username, http.StatusOK, "", start)
if mode == "bind" {
// 绑定模式:版本已递增,新 token 经 fragment 带回设置页无感换发
c.Redirect(http.StatusFound, "/settings?oauth=bound#oauthToken="+url.QueryEscape(token))
@@ -321,6 +326,24 @@ func (h *authxHandler) oauthCallback(c *gin.Context) {
c.Redirect(http.StatusFound, "/login#oauthToken="+url.QueryEscape(token))
}
// recordOauth 把外部登录 / 绑定结果记入系统日志——回调是 GET,
// 不经写方法中间件;实际响应恒为 302,日志按语义记 200 / 401。
func (h *authxHandler) recordOauth(c *gin.Context, username string, status int, errMsg string, start time.Time) {
if h.logs == nil {
return
}
h.logs.Record(model.SystemLog{
Username: username,
Method: c.Request.Method,
Path: requestPath(c),
Status: status,
DurationMs: time.Since(start).Milliseconds(),
ClientIP: requestIP(c),
UserAgent: truncateLogField(c.Request.UserAgent(), 256),
ErrMsg: errMsg,
})
}
// oauthErrText 把流程错误转为用户可读文案;内部错误不透出细节。
func oauthErrText(err error) string {
for _, known := range []error{service.ErrOAuthNotConfigured, service.ErrOAuthNoAppURL, service.ErrOAuthDisabled, service.ErrOAuthState, service.ErrOAuthNotBound, service.ErrOAuthBound} {
+1 -1
View File
@@ -12,7 +12,7 @@ func registerAuthPublic(v1 *gin.RouterGroup, auth *service.AuthService, oauth *s
v1.POST("/auth/login", ah.login)
// 外部身份登录:provider 列表 / 授权跳转(bind 模式 handler 内校验 JWT)/ 回调
ax := &authxHandler{auth: auth, oauth: oauth}
ax := &authxHandler{auth: auth, oauth: oauth, logs: systemLogs}
v1.GET("/auth/oauth/providers", ax.oauthProviders)
v1.GET("/auth/oauth/:provider/authorize", ax.oauthAuthorize)
v1.GET("/auth/oauth/:provider/callback", ax.oauthCallback)