126 lines
3.5 KiB
Go
126 lines
3.5 KiB
Go
package router
|
||
|
||
import (
|
||
"context"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
|
||
"chatapp3-golang/internal/common"
|
||
"chatapp3-golang/internal/integration"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
const authUserContextKey = "auth_user"
|
||
|
||
type authGateway interface {
|
||
AuthenticateToken(ctx context.Context, token string) (integration.UserCredential, error)
|
||
AuthenticateConsoleToken(ctx context.Context, authorization string) (integration.ConsoleAccount, error)
|
||
}
|
||
|
||
// authMiddleware 校验用户 token,并把认证后的用户信息写入上下文。
|
||
func authMiddleware(javaClient authGateway) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
authorization := strings.TrimSpace(c.GetHeader("Authorization"))
|
||
if authorization == "" {
|
||
writeError(c, common.NewAppError(http.StatusUnauthorized, "missing_authorization", "authorization is required"))
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
token := trimBearer(authorization)
|
||
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
|
||
defer cancel()
|
||
|
||
credential, err := javaClient.AuthenticateToken(ctx, token)
|
||
if err != nil {
|
||
writeError(c, common.NewAppError(http.StatusUnauthorized, "invalid_token", err.Error()))
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
c.Set(authUserContextKey, common.AuthUser{
|
||
UserID: int64(credential.UserID),
|
||
SysOrigin: credential.SysOrigin,
|
||
Token: token,
|
||
Authorization: authorization,
|
||
})
|
||
c.Next()
|
||
}
|
||
}
|
||
|
||
// consoleAuthMiddleware 校验后台 console token,供后台管理类 Go 路由使用。
|
||
func consoleAuthMiddleware(javaClient authGateway) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
authorization := strings.TrimSpace(c.GetHeader("Authorization"))
|
||
if authorization == "" {
|
||
writeError(c, common.NewAppError(http.StatusUnauthorized, "missing_authorization", "authorization is required"))
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
|
||
defer cancel()
|
||
|
||
if _, err := javaClient.AuthenticateConsoleToken(ctx, authorization); err != nil {
|
||
writeError(c, common.NewAppError(http.StatusUnauthorized, "invalid_console_token", err.Error()))
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
c.Next()
|
||
}
|
||
}
|
||
|
||
// internalSecretMiddleware 校验内部回调密钥。
|
||
func internalSecretMiddleware(secret string) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
if strings.TrimSpace(secret) == "" {
|
||
c.Next()
|
||
return
|
||
}
|
||
|
||
token := strings.TrimSpace(c.GetHeader("X-Internal-Token"))
|
||
if token != secret {
|
||
writeError(c, common.NewAppError(http.StatusUnauthorized, "invalid_internal_token", "internal token is invalid"))
|
||
c.Abort()
|
||
return
|
||
}
|
||
|
||
c.Next()
|
||
}
|
||
}
|
||
|
||
// mustAuthUser 从 Gin 上下文读取已经通过鉴权的用户。
|
||
func mustAuthUser(c *gin.Context) common.AuthUser {
|
||
value, exists := c.Get(authUserContextKey)
|
||
if !exists {
|
||
return common.AuthUser{}
|
||
}
|
||
user, _ := value.(common.AuthUser)
|
||
return user
|
||
}
|
||
|
||
// trimBearer 去掉 Authorization 里的 Bearer 前缀。
|
||
func trimBearer(authorization string) string {
|
||
if strings.HasPrefix(strings.ToLower(authorization), "bearer ") {
|
||
return strings.TrimSpace(authorization[7:])
|
||
}
|
||
return authorization
|
||
}
|
||
|
||
// resolveClientIP 解析请求来源 IP。
|
||
func resolveClientIP(c *gin.Context) string {
|
||
if realIP := strings.TrimSpace(c.GetHeader("X-Real-IP")); realIP != "" {
|
||
return realIP
|
||
}
|
||
if forwarded := strings.TrimSpace(c.GetHeader("X-Forwarded-For")); forwarded != "" {
|
||
parts := strings.Split(forwarded, ",")
|
||
if len(parts) > 0 {
|
||
return strings.TrimSpace(parts[0])
|
||
}
|
||
}
|
||
return strings.TrimSpace(c.ClientIP())
|
||
}
|