はじめに
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) })
}
}
最後に
間違っている、気になる等ありましたら、優しく諭していただけるととても喜びます。