56 lines
2.0 KiB
Go
56 lines
2.0 KiB
Go
package httpapi
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"net/http"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"data-terminal-cloud/internal/identity"
|
||
|
|
)
|
||
|
|
|
||
|
|
func BearerAuth(verifier identity.TokenVerifier, next http.Handler) http.Handler {
|
||
|
|
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
rawToken, ok := bearerToken(request.Header.Get("Authorization"))
|
||
|
|
if !ok {
|
||
|
|
writer.Header().Set("WWW-Authenticate", "Bearer")
|
||
|
|
WriteError(writer, request, http.StatusUnauthorized, "AUTHENTICATION_REQUIRED", "请先登录后再访问")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
principal, err := verifier.Verify(request.Context(), rawToken)
|
||
|
|
if err != nil {
|
||
|
|
writer.Header().Set("WWW-Authenticate", `Bearer error="invalid_token"`)
|
||
|
|
WriteError(writer, request, http.StatusUnauthorized, "AUTHENTICATION_INVALID", "登录凭证无效或已经过期")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
next.ServeHTTP(writer, request.WithContext(identity.WithPrincipal(request.Context(), principal)))
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func CurrentUserHandler(repository *identity.Repository) http.Handler {
|
||
|
|
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||
|
|
writer.Header().Set("Cache-Control", "no-store")
|
||
|
|
principal, ok := identity.PrincipalFromContext(request.Context())
|
||
|
|
if !ok {
|
||
|
|
WriteError(writer, request, http.StatusUnauthorized, "AUTHENTICATION_REQUIRED", "请先登录后再访问")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
currentUser, err := repository.CurrentUser(request.Context(), principal)
|
||
|
|
switch {
|
||
|
|
case errors.Is(err, identity.ErrAccountNotFound):
|
||
|
|
WriteError(writer, request, http.StatusForbidden, "RESOURCE_ACCESS_DENIED", "当前账号无权访问该资源")
|
||
|
|
case err != nil:
|
||
|
|
WriteError(writer, request, http.StatusInternalServerError, "INTERNAL_ERROR", "服务暂时不可用")
|
||
|
|
default:
|
||
|
|
WriteJSON(writer, request, http.StatusOK, currentUser)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func bearerToken(header string) (string, bool) {
|
||
|
|
parts := strings.Fields(header)
|
||
|
|
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") || strings.TrimSpace(parts[1]) == "" {
|
||
|
|
return "", false
|
||
|
|
}
|
||
|
|
return parts[1], true
|
||
|
|
}
|