package handler import ( "net/http" "time" "github.com/gin-gonic/gin" "github.com/user-management-system/internal/auth" ) // SSOHandler SSO 处理程序 type SSOHandler struct { ssoManager *auth.SSOManager } // NewSSOHandler 创建 SSO 处理程序 func NewSSOHandler(ssoManager *auth.SSOManager) *SSOHandler { return &SSOHandler{ssoManager: ssoManager} } // AuthorizeRequest 授权请求 type AuthorizeRequest struct { ClientID string `form:"client_id" binding:"required"` RedirectURI string `form:"redirect_uri" binding:"required"` ResponseType string `form:"response_type" binding:"required"` Scope string `form:"scope"` State string `form:"state"` } // Authorize 处理 SSO 授权请求 // GET /api/v1/sso/authorize?client_id=xxx&redirect_uri=xxx&response_type=code&scope=openid&state=xxx func (h *SSOHandler) Authorize(c *gin.Context) { var req AuthorizeRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 验证 response_type if req.ResponseType != "code" && req.ResponseType != "token" { c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported response_type"}) return } // 获取当前登录用户(从 auth middleware 设置的 context) userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } username, _ := c.Get("username") // 生成授权码或 access token if req.ResponseType == "code" { code, err := h.ssoManager.GenerateAuthorizationCode( req.ClientID, req.RedirectURI, req.Scope, userID.(int64), username.(string), ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate code"}) return } // 重定向回客户端 redirectURL := req.RedirectURI + "?code=" + code if req.State != "" { redirectURL += "&state=" + req.State } c.Redirect(http.StatusFound, redirectURL) } else { // implicit 模式,直接返回 token code, err := h.ssoManager.GenerateAuthorizationCode( req.ClientID, req.RedirectURI, req.Scope, userID.(int64), username.(string), ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate code"}) return } // 验证授权码获取 session session, err := h.ssoManager.ValidateAuthorizationCode(code) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to validate code"}) return } token, _ := h.ssoManager.GenerateAccessToken(req.ClientID, session) // 重定向回客户端,带 token redirectURL := req.RedirectURI + "#access_token=" + token + "&expires_in=7200" if req.State != "" { redirectURL += "&state=" + req.State } c.Redirect(http.StatusFound, redirectURL) } } // TokenRequest Token 请求 type TokenRequest struct { GrantType string `form:"grant_type" binding:"required"` Code string `form:"code"` RedirectURI string `form:"redirect_uri"` ClientID string `form:"client_id" binding:"required"` ClientSecret string `form:"client_secret" binding:"required"` } // TokenResponse Token 响应 type TokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` ExpiresIn int64 `json:"expires_in"` Scope string `json:"scope"` } // Token 处理 Token 请求(授权码模式第二步) // POST /api/v1/sso/token func (h *SSOHandler) Token(c *gin.Context) { var req TokenRequest if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 验证 grant_type if req.GrantType != "authorization_code" { c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported grant_type"}) return } // 验证授权码 session, err := h.ssoManager.ValidateAuthorizationCode(req.Code) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid code"}) return } // 生成 access token token, expiresAt := h.ssoManager.GenerateAccessToken(req.ClientID, session) c.JSON(http.StatusOK, TokenResponse{ AccessToken: token, TokenType: "Bearer", ExpiresIn: int64(time.Until(expiresAt).Seconds()), Scope: session.Scope, }) } // IntrospectRequest Introspect 请求 type IntrospectRequest struct { Token string `form:"token" binding:"required"` ClientID string `form:"client_id"` } // IntrospectResponse Introspect 响应 type IntrospectResponse struct { Active bool `json:"active"` UserID int64 `json:"user_id,omitempty"` Username string `json:"username,omitempty"` ExpiresAt int64 `json:"exp,omitempty"` Scope string `json:"scope,omitempty"` } // Introspect 验证 access token // POST /api/v1/sso/introspect func (h *SSOHandler) Introspect(c *gin.Context) { var req IntrospectRequest if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } info, err := h.ssoManager.IntrospectToken(req.Token) if err != nil { c.JSON(http.StatusOK, IntrospectResponse{Active: false}) return } c.JSON(http.StatusOK, IntrospectResponse{ Active: info.Active, UserID: info.UserID, Username: info.Username, ExpiresAt: info.ExpiresAt.Unix(), Scope: info.Scope, }) } // RevokeRequest 撤销请求 type RevokeRequest struct { Token string `form:"token" binding:"required"` } // Revoke 撤销 access token // POST /api/v1/sso/revoke func (h *SSOHandler) Revoke(c *gin.Context) { var req RevokeRequest if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } h.ssoManager.RevokeToken(req.Token) c.JSON(http.StatusOK, gin.H{"message": "token revoked"}) } // UserInfoResponse 用户信息响应 type UserInfoResponse struct { UserID int64 `json:"user_id"` Username string `json:"username"` } // UserInfo 获取当前用户信息(SSO 专用) // GET /api/v1/sso/userinfo func (h *SSOHandler) UserInfo(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } username, _ := c.Get("username") c.JSON(http.StatusOK, UserInfoResponse{ UserID: userID.(int64), Username: username.(string), }) }