package auth import ( "context" "encoding/base64" "errors" "fmt" "log/slog" "strings" "time" "github.com/gofiber/fiber/v2" ) func Authenticate() fiber.Handler { return func(ctx *fiber.Ctx) error { header := ctx.Get(fiber.HeaderAuthorization) authCtx, err := authHeader(ctx.Context(), header) if err != nil { return err } if authCtx == nil { authCtx = &AuthCtx{} } SetAuthCtx(ctx, authCtx) return ctx.Next() } } func authHeader(ctx context.Context, header string) (*AuthCtx, error) { if header == "" { return nil, nil } var split = strings.Split(header, " ") if len(split) != 2 { slog.Debug("Authorization 头格式不正确") return nil, ErrAuthenticateUnauthorize } var token = strings.TrimSpace(split[1]) if token == "" { slog.Debug("提供的令牌为空") return nil, ErrAuthenticateUnauthorize } var authCtx *AuthCtx var err error switch split[0] { case "Bearer": authCtx, err = authBearer(ctx, token) if err != nil { slog.Debug("Bearer 认证失败", "err", err) return nil, ErrAuthenticateUnauthorize } case "Basic": authCtx, err = authBasic(ctx, token) if err != nil { slog.Debug("Basic 认证失败", "err", err) return nil, ErrAuthenticateUnauthorize } default: slog.Debug("无效的认证方式", "method", split[0]) return nil, ErrAuthenticateUnauthorize } return authCtx, err } func authBearer(_ context.Context, token string) (*AuthCtx, error) { session, err := FindSession(token, time.Now()) if err != nil { slog.Debug("Bearer 认证失败", "err", err) return nil, ErrAuthenticateUnauthorize } scopes := []string{} if session.Scopes != nil { scopes = strings.Split(*session.Scopes, " ") } return &AuthCtx{ User: session.User, Admin: session.Admin, Client: session.Client, Scopes: scopes, Session: session, }, nil } func authBasic(_ context.Context, token string) (*AuthCtx, error) { // 解析 Basic 认证信息 var base, err = base64.RawURLEncoding.DecodeString(token) if err != nil { base, err = base64.URLEncoding.DecodeString(token) if err != nil { return nil, errors.New("令牌格式错误,无法解析令牌") } } var split = strings.Split(string(base), ":") if len(split) != 2 { return nil, errors.New("令牌格式错误,必须是 : 格式") } client, err := authClient(split[0], split[1]) if err != nil { return nil, fmt.Errorf("客户端认证失败:%w", err) } return &AuthCtx{ Client: client, Scopes: []string{}, }, nil }