8
4

More than 1 year has passed since last update.

[Go]Go製APIサーバで認証処理を作成する

Posted at

はじめに

Go、Ginを使って認証処理を実装していきます
認証情報はCookieを使ってセッションに保持します

環境

Golang 1.16.4
Gin 1.7.4

リクエスト用のモデルを作成します

メールアドレスとパスワードで認証していきます

login.go
type Login struct {
    Email     string    `json:"email" bson:"email"`
    Password  string    `json:"password" bson:"password"`
    UpdatedAt time.Time `json:"updated_at"`
    CreatedAt time.Time `json:"created_at"`
}

ログイン用のhandler

login_handler.go
type LoginHandler interface {
    Login(ctx *gin.Context)
}

type loginHandler struct {
    loginUsecase loginUsecase.LoginUsecase
}

// NewLoginHandler constructor
func NewLoginHandler(loginUsecase loginUsecase.LoginUsecase) LoginHandler {
    return &loginHandler{loginUsecase: loginUsecase}
}

func (loginHandler *loginHandler) Login(ctx *gin.Context) {
    var request domain.Login
    err := ctx.BindJSON(&request)
    if err != nil {
        ctx.JSON(http.StatusBadRequest, delivery.NewH(http.StatusText(http.StatusBadRequest), http.StatusBadRequest))
    } else {
        // メールアドレスでユーザ情報取得
        user, err := loginHandler.loginUsecase.GetByEmail(request.Email)
        if err != nil {
            ctx.JSON(http.StatusInternalServerError, delivery.NewH(err.Error(), nil))
            return
        }

        // ハッシュ値でのパスワード比較
        err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(request.Password))
        if err != nil {
            ctx.JSON(http.StatusBadRequest, delivery.NewH(err.Error(), http.StatusBadRequest))
        } else {
            session := sessions.Default(ctx)
            // セッションに格納する為にユーザ情報をJson化
            loginUser, err := json.Marshal(user)
            if err == nil {
                u, _ := uuid.NewRandom()
                accessToken := u.String()
                session.Set(accessToken, string(loginUser))
                session.Save()
                ctx.SetSameSite(http.SameSiteDefaultMode)

                ctx.JSON(http.StatusOK, delivery.NewH(http.StatusText(http.StatusOK), accessToken))
            } else {
                ctx.JSON(http.StatusInternalServerError, delivery.NewH(err.Error(), http.StatusInternalServerError))
            }
        }
    }
}

ミドルウェア

認証ガード用のミドルウェアを作成していきます

auth_middleware.go
func LoginCheckMiddleware() gin.HandlerFunc {
    return func(ctx *gin.Context) {
        accessToken := ctx.Request.Header.Get("accessToken")
        session := sessions.Default(ctx)
        // Json文字列がinterdace型で格納されている。dproxyのライブラリを使用して値を取り出す
        loginUserJson, err := dproxy.New(session.Get(accessToken)).String()

        if err != nil {
            ctx.Status(http.StatusUnauthorized)
            ctx.Abort()
        } else {
            var loginInfo domain.User
            // Json文字列のアンマーシャル
            err := json.Unmarshal([]byte(loginUserJson), &loginInfo)
            if err != nil {
                ctx.Status(http.StatusUnauthorized)
                ctx.Abort()
            } else {
                ctx.Next()
            }
        }
    }
}

ルーティングの設定

CORSの設定をしていきます
OriginをまたいだXMLHttpRequestでCookie を送りたい場合、
Cookieの送受信を許可するために、クライアントサイド・サーバーサイドに実装が必要になります

CORSに関して
https://qiita.com/att55/items/2154a8aad8bf1409db2b

また、authUserGroup := r.Gin.Group("/auth")以下で、
GinのGroup機能を使って、認証ガードをかけています

routing.go
func NewRouting(db *DB) *Routing {
    r := &Routing{
        DB:  db,
        Gin: gin.Default(),
    }
    // Corsの設定
    r.Gin.Use(cors.New(cors.Config{
        // 許可アクセス元
        AllowOrigins: []string{
            "https://hogehoge.com",
        },
        // アクセス許可HTTPメソッド(以下PUT,DELETEアクセス不可)
        AllowMethods: []string{
            "POST",
            "GET",
            "OPTIONS",
        },
        // 許可HTTPリクエストヘッダ
        AllowHeaders: []string{
            "Access-Control-Allow-Credentials",
            "Access-Control-Allow-Headers",
            "Content-Type",
            "Content-Length",
            "Accept-Encoding",
            "Authorization",
            "accessToken",
            "Set-Cookie",
            "Cookie",
        },
        // cookie必要許可
        AllowCredentials: true,
        // preflightリクエストの結果をキャッシュする時間
        MaxAge: 24 * time.Hour,
    }))

    // セッションCookieの設定
    store := cookie.NewStore([]byte("secret"))
    store.Options(sessions.Options{
        Secure:   false,
        HttpOnly: false})
    r.Gin.Use(sessions.Sessions("sampleAppSessKey", store))

    r.setRouting()
    return r
}

func (r *Routing) setRouting() {
    // 認証済のみアクセス可能なグループ
    authUserGroup := r.Gin.Group("/auth")
    authUserGroup.Use(middleware.LoginCheckMiddleware())
    {
        r.Gin.GET("/", func(ctx *gin.Context) { sampleHandler.GetAll(ctx) })
    }
}

最後に

間違っている、気になる等ありましたら、優しく諭していただけるととても喜びます。

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4