Serie: Go
go
77 líneas
· Actualizado 2026-04-18
auth_handler.go
Go/Baasid/internal/handlers/auth_handler.go
package handlers
import (
"errors"
"net/http"
"baasid/internal/auth"
"baasid/internal/dto"
"baasid/internal/repository"
"github.com/labstack/echo/v4"
"gorm.io/gorm"
)
// AuthHandler implements POST /auth/login.
type AuthHandler struct {
users *repository.UserRepository
tokens *auth.TokenService
}
// NewAuthHandler wires dependencies for login.
func NewAuthHandler(users *repository.UserRepository, tokens *auth.TokenService) *AuthHandler {
return &AuthHandler{users: users, tokens: tokens}
}
// Login godoc
// @Summary 用戶登入
// @Description 驗證帳號密碼後簽發 JWT。成功時於 **Response Header** 的 `Authorization` 欄位回傳 Bearer Token,Body 回傳帳號與姓名。
// @Tags auth
// @Accept json
// @Produce json
// @Param body body dto.LoginRequest true "登入帳號與密碼"
// @Success 200 {object} dto.LoginResponse "成功;並於 Header `Authorization` 帶入 JWT"
// @Failure 401 {object} dto.ErrorMessage "帳號密碼錯誤"
// @Failure 500 {object} dto.ErrorMessage "系統錯誤"
// @Router /auth/login [post]
func (h *AuthHandler) Login(c echo.Context) error {
var req dto.LoginRequest
// Bind automatically unmarshals JSON and respects the `json` struct tags.
if err := c.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, dto.ErrorMessage{Message: "無效的 JSON 格式"})
}
if req.Account == "" || req.Password == "" {
return echo.NewHTTPError(http.StatusBadRequest, dto.ErrorMessage{Message: "account 與 password 不可為空"})
}
u, err := h.users.FindByAccount(req.Account)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// Use the same response for unknown account and wrong password so an
// attacker cannot enumerate which accounts exist.
return echo.NewHTTPError(http.StatusUnauthorized, dto.ErrorMessage{Message: "帳號密碼錯誤"})
}
// Any other database error is treated as an internal failure.
c.Logger().Errorf("login db error: %v", err)
return echo.NewHTTPError(http.StatusInternalServerError, dto.ErrorMessage{Message: "系統錯誤"})
}
if !auth.CheckPassword(u.Password, req.Password) {
return echo.NewHTTPError(http.StatusUnauthorized, dto.ErrorMessage{Message: "帳號密碼錯誤"})
}
token, err := h.tokens.SignAccessToken(u.ID, u.Account)
if err != nil {
c.Logger().Errorf("sign jwt: %v", err)
return echo.NewHTTPError(http.StatusInternalServerError, dto.ErrorMessage{Message: "系統錯誤"})
}
// Requirement: client reads JWT from response header "Authorization".
// We use the standard Bearer scheme so browsers and proxies recognise it.
c.Response().Header().Set(echo.HeaderAuthorization, "Bearer "+token)
return c.JSON(http.StatusOK, dto.LoginResponse{
Account: u.Account,
Name: u.Name,
})
}
Artículos relacionados
Go
go
Actualizado 2026-04-18
main.go
main.go — go source code from the Go learning materials (Go/Baasid/cmd/hashpw/main.go).
Leer artículo →
Go
go
Actualizado 2026-04-18
main.go
main.go — go source code from the Go learning materials (Go/Baasid/cmd/server/main.go).
Leer artículo →
Go
go
Actualizado 2026-04-18
docs.go
docs.go — go source code from the Go learning materials (Go/Baasid/docs/docs.go).
Leer artículo →
Go
go
Actualizado 2026-04-18
jwt.go
jwt.go — go source code from the Go learning materials (Go/Baasid/internal/auth/jwt.go).
Leer artículo →
Go
go
Actualizado 2026-04-18
password.go
password.go — go source code from the Go learning materials (Go/Baasid/internal/auth/password.go).
Leer artículo →
Go
go
Actualizado 2026-04-18
password_test.go
password_test.go — go source code from the Go learning materials (Go/Baasid/internal/auth/password_test.go).
Leer artículo →