Web サイトのバックエンドを作成するときによく耳にする非常に重要な用語の 1 つは、JWT 認証です。 JWT 認証は、API を保護する最も一般的な方法の 1 つです。 JWT は JSON Web Token の略で、関係者間で情報を JSON オブジェクトとして安全に送信する方法を定義するオープン標準です。この記事では、JWT 認証について説明します。そして最も重要なことは、Gin-Gonic を使用して高速かつ効率的な JWT 認証のためのプロジェクト全体を作成することです。これは、非常に高速で業界のWeb サイトまたはアプリケーションのバックエンド用の -レベルの認証 API。
目次:
JWT は JSON Web Token の略で、関係者間で情報を JSON オブジェクトとして安全に送信する方法を定義するオープン標準です。
例を使ってそれを理解してみましょう。ホテルに到着してフロントデスクに行くと、受付係が「ねえ、何をしてもらえますか?」と言ったときの状況を考えてみましょう。 「こんにちは、私の名前はシュバムです。カンファレンスに来ています。ホテル代はスポンサーが払ってくれています。」と言うでしょう。受付係は、「わかりました。それでは、いくつか確認する必要があります。」と言います。通常、彼らは私が誰であるかを証明するために私の身分証明書を見る必要があり、私が正しい人物であることが確認されたら、私に鍵を発行します。また、認証はこの例と非常によく似た方法で機能します。
JWT 認証では、サーバーに「こんにちは、私のユーザー名とパスワード、またはサインイン トークンはこれです」というリクエストを送信し、Web サイトは「わかりました、確認させてください」と表示します。ユーザー名とパスワードが正しければ、トークンが得られます。これで、その後のサーバーのリクエストでは、ユーザー名とパスワードを含める必要がなくなりました。トークンを持ち歩いてホテル (ウェブサイト) にチェックインし、ジム (データ) にアクセスします。プール (情報) にアクセスでき、ホテルの部屋 (アカウント) にのみアクセスできます。他のホテルの部屋 (他のユーザーのアカウント)。このトークンは、私の州の期間中、つまりチェックイン時間からチェックアウト時間までの間のみ許可されます。それ以降は役に立ちません。今後、このホテルでは、認証のない人々でも、少なくともホテルを見ること、ホテルに入るまでホテル周辺の公共エリアを歩き回ることが許可されます。同様に、匿名ユーザーであるあなたは、ウェブサイトのホームページやランディング ページと対話することができます、など
JWT の例を次に示します:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
これがプロジェクトの構造です。ワークスペースにも同様のフォルダー構造を作成してください。この構造には 6 つのフォルダーがあります:
これらのフォルダー内にそれぞれのファイルを作成します。
ステップ 1. モジュールを作成してプロジェクトを開始しましょう。モジュールの名前は「jwt」、ユーザー名は「1shubham7」です。したがって、次のように入力してモジュールを初期化します。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
これにより go.mod ファイルが作成されます。
ステップ 2. main.go ファイルを作成し、main.go 内に Web サーバーを作成しましょう。そのためには、次のコードをファイルに追加します:
go mod init github.com/1shubham7/jwt
環境変数を取得するための「os」パッケージ。
gin-gonic パッケージを使用して Web サーバーを作成しています。
後でルート パッケージを作成します。
AuthRoutes()、UserRoutes() は、routes パッケージのファイル内の関数です。後で作成します。
ステップ 3. gin-gonic パッケージをダウンロードします:
package main import( "github.com/gin-gonic/gin" "os" routes "github.com/1shubham7/jwt/routes" "github.com/joho/godotenv" "log" ) func main() { err := godotenv.Load(".env") if err != nil { log.Fatal("Error locading the .env file") } port := os.Getenv("PORT") if port == "" { port = "1111" } router := gin.New() router.Use(gin.Logger()) routes.AuthRoutes(router) routes.UserRoutes(router) // creating two APIs router.GET("/api-1", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-1"}) }) router.GET("api-2", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-2"}) }) router.Run(":" + port) }
ステップ 4. モデルフォルダーを作成し、その中に userModel.go ファイルを作成します。 userModel.go 内に次のコードを入力します:
go get github.com/gin-gonic/gin
User という構造体を作成し、必要なフィールドをその構造体に追加しました。
json:"first_name" validate:"required, min=2, max=100" これはフィールド タグと呼ばれ、go コードの JSON へのデコードおよびエンコード、および JSON to go の際に使用されます。
ここで validate:"required, min=2, max=100" は、特定のフィールドが最小 2 文字、最大 100 文字でなければならないことを検証するために使用されます。
ステップ 5. データベース フォルダーを作成し、その中にdatabaseConnection.go ファイルを作成し、その中に次のコードを入力します。
package models import ( "go.mongodb.org/mongo-driver/bson/primitive" "time" ) type User struct { ID primitive.ObjectID `bson:"id"` First_name *string `json:"first_name" validate:"required, min=2, max=100"` Last_name *string `json:"last_name" validate:"required, min=2, max=100"` Password *string `json:"password" validate:"required, min=6"` Email *string `json:"email" validate:"email, required"` //validate email means it should have an @ Phone *string `json:"phone" validate:"required"` Token *string `json:"token"` User_type *string `json:"user_type" validate:"required, eq=ADMIN|eq=USER"` Refresh_token *string `json:"refresh_token"` Created_at time.Time `json:"created_at"` Updated_at time.Time `json:"updated_at"` User_id string `json:"user_id"` }
「mongo」パッケージも必ずダウンロードしてください:
package database import ( "fmt" "log" "os" "time" "context" "github.com/joho/godotenv" "go.mongodb.org/mongo-driver/mongo" "go/mongodb.org/mongo-driver/mongo/options" ) func DBinstance() *mongo.Client{ err := godotenv.Load(".env") if err != nil { log.Fatal("Error locading the .env file") } MongoDb := os.Getenv("THE_MONGODB_URL") client, err := mongo.NewClient(options.Client().ApplyURI(MongoDb)) if err != nil { log.Fatal(err) } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() err = client.Connect(ctx) if err!=nil{ log.Fatal(err) } fmt.Println("Connected to MongoDB!!") return client } var Client *mongo.Client = DBinstance() func OpenCollection(client *mongo.Client, collectionName string) *mongo.Collection { var collection *mongo.Collection = client.Database("cluster0").Collection(collectionName) return collection }
ここでは、mongo データベースをアプリケーションに接続しています。
メインディレクトリの .env ファイルに設定する環境変数をロードするために「godotenv」を使用しています。
DBinstance 関数では、.env ファイルから「THE_MONGODB_URL」の値を取得し (次の手順で作成します)、その値を使用して新しい mongoDB クライアントを作成します。
「context」は 10 秒のタイムアウトを持つために使用されます。
OpenCollection Function() は、クライアントと collectionName を入力として受け取り、そのコレクションを作成します。
ステップ 6. ルートについては、authRouter と userRouter という 2 つの異なるファイルを作成します。 authRouter には '/signup' と '/login' が含まれます。これらは全員に公開され、自分自身を承認できるようになります。 userRouter は全員に公開されるわけではありません。これには、「/users」と「/users/:user_id」が含まれます。
routes という名前のフォルダーを作成し、そこに 2 つのファイルを追加します。
次のコードを userRouter.go に入力します:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
ステップ 7. 次のコードを authRouter.go に入力します。
go mod init github.com/1shubham7/jwt
ステップ 8. コントローラーというフォルダーを作成し、そこに「userController.go」というファイルを追加します。その中に次のコードを入力します。
package main import( "github.com/gin-gonic/gin" "os" routes "github.com/1shubham7/jwt/routes" "github.com/joho/godotenv" "log" ) func main() { err := godotenv.Load(".env") if err != nil { log.Fatal("Error locading the .env file") } port := os.Getenv("PORT") if port == "" { port = "1111" } router := gin.New() router.Use(gin.Logger()) routes.AuthRoutes(router) routes.UserRoutes(router) // creating two APIs router.GET("/api-1", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-1"}) }) router.GET("api-2", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-2"}) }) router.Run(":" + port) }
これら 2 つの変数は、前のコードですでに使用されています。
ステップ 10. まず、GetUserById() 関数を作成しましょう。 GetUserById() 関数に次のコードを入力します:
go get github.com/gin-gonic/gin
ステップ 11. helpers というフォルダーを作成し、その中に authHelper.go というファイルを追加します。次のコードを authHelper.go に入力します:
package models import ( "go.mongodb.org/mongo-driver/bson/primitive" "time" ) type User struct { ID primitive.ObjectID `bson:"id"` First_name *string `json:"first_name" validate:"required, min=2, max=100"` Last_name *string `json:"last_name" validate:"required, min=2, max=100"` Password *string `json:"password" validate:"required, min=6"` Email *string `json:"email" validate:"email, required"` //validate email means it should have an @ Phone *string `json:"phone" validate:"required"` Token *string `json:"token"` User_type *string `json:"user_type" validate:"required, eq=ADMIN|eq=USER"` Refresh_token *string `json:"refresh_token"` Created_at time.Time `json:"created_at"` Updated_at time.Time `json:"updated_at"` User_id string `json:"user_id"` }
MatchUserTypeToUserId() 関数は、ユーザーが管理者であるか、単なるユーザーであるかを照合します。
MatchUserTypeToUserId() 内で CheckUserType() 関数を使用しています。これは、すべてが正常かどうか (ユーザーから取得した user_type が userType 変数と同じかどうか) をチェックしているだけです。
ステップ 12. これで、userController.go の SignUp() 関数を操作できるようになります:
package database import ( "fmt" "log" "os" "time" "context" "github.com/joho/godotenv" "go.mongodb.org/mongo-driver/mongo" "go/mongodb.org/mongo-driver/mongo/options" ) func DBinstance() *mongo.Client{ err := godotenv.Load(".env") if err != nil { log.Fatal("Error locading the .env file") } MongoDb := os.Getenv("THE_MONGODB_URL") client, err := mongo.NewClient(options.Client().ApplyURI(MongoDb)) if err != nil { log.Fatal(err) } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() err = client.Connect(ctx) if err!=nil{ log.Fatal(err) } fmt.Println("Connected to MongoDB!!") return client } var Client *mongo.Client = DBinstance() func OpenCollection(client *mongo.Client, collectionName string) *mongo.Collection { var collection *mongo.Collection = client.Database("cluster0").Collection(collectionName) return collection }
タイプ User の変数 user を作成しました。
validationErr は構造体タグを検証するために使用されます。これについてはすでに説明しました。
検証には count 変数も使用しています。ユーザーの電子メールを含むドキュメントがすでに見つかった場合と同様に、カウントは 0 より大きくなり、そのエラー (err)
次に、「time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))」を使用して、Created_at、Updated_at 構造体フィールドの時刻を設定します。ユーザーがサインアップしようとすると、関数 SignUp() が実行され、その特定の時間が Created_at 構造体フィールドと Updated_at 構造体フィールドに保存されます。
次に、GenerateAllTokens() 関数を使用してトークンを作成します。この関数は、次のステップで同じパッケージ内の tokenHelper.go ファイルに作成します。
また、user.password をハッシュし、user.password をハッシュされたパスワードに置き換えるだけの HashPassword() 関数もあります。それも後で作成します。
そして、データとトークンなどを userCollection
すべてがうまくいけば、StatusOK を返します。
ステップ 13. 「tokenHelper.go」という名前のヘルパーフォルダーにファイルを作成し、その中に次のコードを入力します。
go get go.mongodb.org/mongo-driver/mongo
github.com/dgrijalva/jwt-go パッケージも必ずダウンロードしてください:
package routes import ( "github.com/gin-gonic/gin" controllers "github.com/1shubham7/jwt/controllers" middleware "github.com/1shubham7/jwt/middleware" ) // user should not be able to use userRoute without the token func UserRoutes (incomingRoutes *gin.Engine) { incomingRoutes.Use(middleware.Authenticate()) // user routes are public routes but these must be authenticated, that // is why we have Authenticate() before these incomingRoutes.GET("/users", controllers.GetUsers()) incomingRoutes.GET("users/:user_id", controllers.GetUserById()) }
ここでは、トークンの生成に github.com/dgrijalva/jwt-go を使用しています。
トークンの生成に必要なフィールド名を含む SignedDetails という構造体を作成しています。
NewWithClaims を使用して新しいトークンを生成し、値を Claim 構造体と RefreshClaims 構造体に渡しています。 claims には初めてのユーザー用のトークンがあり、refreshClaims にはユーザーがトークンを更新する必要があるときにトークンが含まれます。つまり、以前は有効期限が切れたトークンを持っていました。
time.Now().Local().Add(time.Hour *time.Duration(120)).Unix() は、トークンの有効期限を設定するために使用されます。
その後、SignUp() 関数で使用している token、refreshToken、err という 3 つのものを単純に返します。
ステップ 14. SignUp() 関数と同じファイルに、ステップ 9 で説明した HashPassword() 関数を作成しましょう。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
実際のパスワードからハッシュされたパスワードを生成するために使用される bcrypt パッケージの GenerateFromPassword() メソッドを使用しているだけです。
ユーザーのプライバシーのためにも、ハッカーがシステムに侵入してすべてのパスワードを盗むことを望まないため、これは重要です。
[]byte (バイト配列) は単なる文字列です。
ステップ 15. 「userController.go」に Login() 関数を作成しましょう。後のステップで、Login() が使用する関数を作成できます。
go mod init github.com/1shubham7/jwt
タイプ User の 2 つの変数、user と Founduser を作成しています。そしてリクエストからのデータをユーザーに渡します。
'userCollection.FindOne(ctx, bson.M{"email": user.Email}).Decode(&foundUser)' の助けにより、電子メールを通じてユーザーを検索し、見つかった場合は、それを foundUser 変数に保存します。
次に、VerifyPassword() 関数を使用してパスワードを検証し、保存します。VerifyPassword() でパラメータとしてポインタを取得していることに注意してください。そうでない場合は、パラメータの新しいインスタンスが作成されます。実際に変更します。
次のステップで VerifyPassword() を作成します。
次に、GenerateAllTokens() と UpdateAllTokens() を使用して、トークンと RefreshToken (基本的にはトークン) を生成および更新します。
そしてすべてのステップで、私たちは皆エラーを処理しています。
ステップ 16. Login() 関数と同じファイルに VerifyPassword() 関数を作成しましょう:
package main import( "github.com/gin-gonic/gin" "os" routes "github.com/1shubham7/jwt/routes" "github.com/joho/godotenv" "log" ) func main() { err := godotenv.Load(".env") if err != nil { log.Fatal("Error locading the .env file") } port := os.Getenv("PORT") if port == "" { port = "1111" } router := gin.New() router.Use(gin.Logger()) routes.AuthRoutes(router) routes.UserRoutes(router) // creating two APIs router.GET("/api-1", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-1"}) }) router.GET("api-2", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-2"}) }) router.Run(":" + port) }
ここでは、ハッシュされたパスワードを比較するために使用される bcrypt パッケージの CompareHashAndPassword() メソッドを単に使用しています。そして結果に応じてブール値を返します。
[]byte (バイト配列) は単なる文字列ですが、[]byte は比較に役立ちます。
ステップ 17. 「tokenHelper.go」ファイルに UpdateAllTokens() 関数を作成しましょう:
go get github.com/gin-gonic/gin
primitive.D 型の updateObj という変数を作成しています。 MongoDB の Go ドライバーの primitive.D 型は、BSON ドキュメントの表現です。
Append() は、実行されるたびにキーと値のペアを updateObj に追加します。
次に、「time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))」を使用して、現在時刻 (更新が発生して関数が実行された時刻) を Updated_at に更新します。 .
コード ブロックの残りの部分は、mongoDB コレクションの UpdateOne メソッドを使用して更新を実行しています。
最後のステップでは、エラーが発生した場合に備えてエラーも処理します。
ステップ 18. 先に進む前に、go.mongodb.org/mongo-driver パッケージをダウンロードしましょう:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
ステップ 19. 次に、GetUserById() 関数に取り組んでみましょう。 GetUserById() はユーザーが独自の情報にアクセスするためのものであり、管理者はすべてのユーザー データにアクセスでき、ユーザーは自分のデータにのみアクセスできることに注意してください。
go mod init github.com/1shubham7/jwt
リクエストから user_id を取得し、userId 変数に保存します。
タイプ User の user という変数を作成します。次に、user_id を使用してデータベース内のユーザーを検索するだけです。user_id が一致する場合は、その人の情報をユーザー変数に保存します。
すべてがうまくいけば、StatusOk
各ステップでのエラーも処理しています。
ステップ 20. 次に、GetUsers() 関数に取り組んでみましょう。 GetUsers() 関数にはすべてのユーザーのデータが含まれるため、管理者のみがこの関数にアクセスできることに注意してください。 GetUserById()、Login()、および SignUp() 関数と同じファイルに GetUsers() 関数を作成します:
package main import( "github.com/gin-gonic/gin" "os" routes "github.com/1shubham7/jwt/routes" "github.com/joho/godotenv" "log" ) func main() { err := godotenv.Load(".env") if err != nil { log.Fatal("Error locading the .env file") } port := os.Getenv("PORT") if port == "" { port = "1111" } router := gin.New() router.Use(gin.Logger()) routes.AuthRoutes(router) routes.UserRoutes(router) // creating two APIs router.GET("/api-1", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-1"}) }) router.GET("api-2", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-2"}) }) router.Run(":" + port) }
まず、リクエストが管理者からのものであるかどうかを確認します。これは、前の手順で作成した CheckUserType() を使用して行います。
次に、ページごとに必要なレコード数を設定します。
リクエストから recodePerPage を取得し、それを int に変換することでこれを行うことができます。これは srtconv によって行われます。
recordPerPage の設定でエラーが発生した場合、または RecordPerPage が 1 未満の場合、デフォルトでは 1 ページあたり 9 レコードになります
同様に、変数 'page' でページ番号を取得します。
デフォルトでは、ページ番号は 1 と 9 RecordPerPage になります。
次に、3 つのステージ (matchStage、groupStage、projectStage) を作成しました。
次に、Aggregate() 関数を使用して Mongo パイプラインにこれら 3 つのステージを設定します
また、すべてのステップでエラーを処理しています。
ステップ 21. 「userController.go」の準備が整いました。完了後の「userController.go」ファイルは次のようになります。
go get github.com/gin-gonic/gin
ステップ 22. これで、認証部分に取り組むことができます。そのために、認証ミドルウェアを作成します。 「middleware」というフォルダーを作成し、その中に「authMiddleware.go」というファイルを作成します。ファイルに次のコードを入力します:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
ステップ 23. 次に、ValidateToken() 関数を作成しましょう。この関数は「tokenHelper.go」に作成します。
go mod init github.com/1shubham7/jwt
ValidateToken は signedToken を受け取り、その SignedDetails をエラー メッセージとともに返します。エラーがなければ「」。
ParseWithClaims() 関数を使用してトークンを取得し、それを token という変数に保存します。
次に、トークンの Claims メソッドを使用して、トークンが正しいかどうかを確認します。そして結果をclaim変数に保存しています。
次に、ExpiresAt() 関数を使用してトークンの有効期限が切れているかどうかを確認します。現在の時刻が ExpiresAt 時刻より大きい場合は、トークンの有効期限が切れていることになります。
そして、メッセージだけでなくクレーム変数も返します。
ステップ 24. これでほとんどの作業が完了しました。「go mod tiny」を実行しましょう。このコマンドは go.mod ファイルをチェックインし、インストールしたが使用していないすべてのパッケージ/依存関係を削除します。使用しているがまだダウンロードしていない依存関係をダウンロードします。
package main import( "github.com/gin-gonic/gin" "os" routes "github.com/1shubham7/jwt/routes" "github.com/joho/godotenv" "log" ) func main() { err := godotenv.Load(".env") if err != nil { log.Fatal("Error locading the .env file") } port := os.Getenv("PORT") if port == "" { port = "1111" } router := gin.New() router.Use(gin.Logger()) routes.AuthRoutes(router) routes.UserRoutes(router) // creating two APIs router.GET("/api-1", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-1"}) }) router.GET("api-2", func(c *gin.Context){ c.JSON(200, gin.H{"success":"Access granted for api-2"}) }) router.Run(":" + port) }
これで JWT 認証プロジェクトの準備が整いました。最後にアプリケーションを実行するには、ターミナルで次のコマンドを入力します。
go get github.com/gin-gonic/gin
同様の出力が得られます:
これによりサーバーが起動して実行され、リクエストの送信とレスポンスの受信にcurlまたはPostman APIを使用できるようになります。または、この API をフロントエンド フレームワークと単純に統合することもできます。これで、認証 API の準備が整いました。ご安心ください。
この記事では、JWT 認証を作成する最も速い方法の 1 つについて説明しました。プロジェクトには、Gin-Gonic フレームワークを使用しました。これは「単なる別の認証 API」ではありません。 Gin は NodeJS より 300% 高速であるため、この認証 API は非常に高速かつ効率的になります。私たちが使用しているプロジェクト構造も業界レベルのプロジェクト構造です。 SECRET_KEY を .env ファイルに保存するなど、さらに多くの変更を加えて、この API を改善することができます。このプロジェクトのソース コードは、1Shubham7/go-jwt-token.
で見つけることもできます。プロジェクトを作成して機能を追加するには、必ずすべての手順に従ってください。コードを試して理解を深めてください。認証を学ぶ最良の方法は、独自のプロジェクトを作成することです。
以上がGo (Golang) で JWT 認証を実装するためのステップバイステップ ガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。