Anne  de Morel

Anne de Morel

1656762060

Mise En œuvre De L'authentification JWT Dans Les API Golang REST

Dans cet article, nous allons apprendre à implémenter l'authentification JWT dans les API Golang REST et à la sécuriser avec le middleware d'authentification. Nous allons construire une API REST Golang simple mais bien organisée avec des packages tels que Gin pour le routage (principalement), GORM pour la persistance des données utilisateur dans une base de données MySQL, etc. Nous allons également créer un middleware qui peut sécuriser les points de terminaison et autoriser les demandes qui ont un JWT valide dans l'en-tête d'autorisation de la demande.

Dans un article précédent, nous avons appris comment implémenter CRUD dans l'API Golang REST avec Mux & GORM. C'est une bonne référence pour démarrer avec les API dans Golang. Lire ici .

Vous pouvez trouver le code source complet de l'authentification JWT dans l'implémentation Golang ici .

JWT expliqué

Donc, avant de commencer l'implémentation, qu'est-ce qu'un JWT ou un jeton Web JSON ?

Vous pouvez ignorer cette section si vous savez déjà ce qu'est un JWT et ce qu'il fait.

Ces jetons sont utilisés par les API RESTful pour l'authentification et l'échange d'informations lorsqu'il n'est pas nécessaire de gérer l'état de l'application.

Selon jwt.io ,

Les jetons Web JSON sont une méthode RFC 7519 ouverte et standard de l'industrie   pour représenter les revendications en toute sécurité entre deux parties.

JWT est un très bon concurrent pour sécuriser les API REST. Voyons la structure d'un JWT réel en action. Rendez-vous sur jwt.io où il y a un exemple de JWT pour notre compréhension.

image 53 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Vous pouvez voir que ces jetons sont séparés en 3 parties avec un point.

  1. Entête
  2. Charge utile
  3. Signature

L'en-tête contient l'algorithme de signature utilisé tel que RSA ou HMAC SHA256. La charge utile contient les données à échanger. Il s'agit généralement de réclamations envoyées par le serveur, telles que le nom d'utilisateur, l'e-mail, etc. Notez que les données sensibles telles que les mots de passe ne doivent jamais être envoyées via un jeton Web JSON. La troisième partie du jeton, qui est la signature, est utilisée pour vérifier que le JWT n'a pas été falsifié.

Premiers pas avec l'authentification JWT dans Golang

Commençons par implémenter l'authentification JWT dans l'API Golang REST. Visual Code sera l'IDE de choix pour l'article (et probablement tous les autres articles Golang que je publierai à l'avenir), en raison de sa facilité d'utilisation et de sa productivité. Assurez-vous d'avoir le dernier SDK stable de Golang. La dernière version disponible lors de la rédaction de cet article est Go 1.18.1.

Assurez-vous également que vous avez installé l'extension Golang sur VS Code, ce qui aide énormément à améliorer l'expérience de développement de Golang. Une autre raison vraiment intéressante d'utiliser VSCode pour le développement d'API est la possibilité d'envoyer des requêtes à l'API directement depuis l'interface VS Code en utilisant les conventions de l'API REST. Permet de gagner beaucoup de temps, plutôt que de passer au facteur ou à d'autres clients REST.

Ouvrez un nouveau dossier dans VS Code et entrez ce qui suit pour initialiser les dépendances du projet Golang.

go mod init jwt-authentication-golang

Ici, créez un fichier main.go avec le code passe-partout suivant.

package main
func main() {
}

Qu'allons-nous construire ?

Permettez-moi de donner un bref aperçu de ce que nous allons construire. Il s'agira donc d'une API REST avec les contrôleurs suivants.

  1. Contrôleur de jetons – Celui-ci aura un point de terminaison qui sera utilisé pour générer les JWT. Ici, l'utilisateur doit envoyer une liste d'e-mails/mots de passe valides.
  2. Contrôleur d'utilisateur - Il y aura un point de terminaison "enregistrer l'utilisateur" qui peut être utilisé pour créer de nouveaux utilisateurs.
  3. Contrôleur sécurisé - Un contrôleur factice qui sera sécurisé par l'authentification JWT. Ceci est juste pour montrer la capacité du middleware que nous allons construire pour restreindre l'accès aux seules demandes qui ont un JWT valide dans l'en-tête de la demande.

Nous ajouterons également des assistants pour JWT qui nous aideront à générer les jetons avec des délais d'expiration et des revendications appropriés, ainsi qu'un moyen de valider les jetons envoyés. Ceci sera utilisé par notre middleware personnalisé pour restreindre l'accès. De plus, comme mentionné précédemment, lors du processus d'inscription, nous stockerons les données de l'utilisateur dans une base de données MySQL à l'aide de l'abstraction GORM. Ici, nous utiliserons un groupe d'assistants pour chiffrer/hacher les mots de passe des utilisateurs. Nous ne voulons pas stocker le mot de passe réel directement dans la base de données, n'est-ce pas ?

Gin – Introduction rapide

Je suis tombé sur Gin, qui est un framework de développement Web pour les API Golang. Ils s'annoncent 40 fois plus rapides que les routeurs HTTP normaux. Ils sont également assez populaires, avec plus de 55 000 étoiles sur Github. J'ai aimé leur outillage et l'amélioration de l'expérience de développement. Gin est un framework qui réduit le code passe-partout qui entrerait normalement dans la construction de ces applications. Exécutez ce qui suit pour installer gin sur votre machine et utilisez-le pour des projets golang.

go get -u github.com/gin-gonic/gin

Pour cet article, nous utiliserons les routeurs Gin et l'implémentation du middleware. Dans les prochains articles, nous explorerons davantage le framework Gin.

Configuration de la base de données et migrations

Tout d'abord, créons un dossier nommé models et un nouveau fichier pour le modèle utilisateur. Appelons-le user.go

type User struct {
gorm.Model
Name string `json:"name"`
Username string `json:"username" gorm:"unique"`
Email string `json:"email" gorm:"unique"`
Password string `json:"password"`
}

Comme vous pouvez le voir, notre modèle d'utilisateur aura un nom, un nom d'utilisateur, un e-mail et un mot de passe. Ici, le nom d'utilisateur et l'e-mail seront uniques. Cela signifie qu'une fois que nous aurons terminé notre application et essayé d'enregistrer de nouveaux utilisateurs avec le même nom d'utilisateur ou e-mail, le code ne vous permettra pas de le faire. La meilleure partie est que vous n'avez pas à écrire de code spécifiquement pour cela. Tout est géré par GORM.

La spécification gorm.Model ajoute certaines propriétés par défaut au modèle, comme l'identifiant, la date de création, la date de modification et la date de suppression.

Notez que, plus tard dans l'article, nous ajouterons quelques assistants à ce fichier go pour nous aider dans le hachage et la validation du mot de passe à l'aide d'un package de chiffrement de golang.

Ensuite, configurons le client qui nous aide à nous connecter à la base de données MySQL. Je suppose que vous avez déjà une instance MySQL en cours d'exécution sur votre ordinateur local au port 3306, qui est le port MySQL par défaut.

Installons les packages Golang requis. Exécutez les commandes suivantes.

go get gorm.io/gorm
go get gorm.io/driver/mysql

Cela installera les packages GORM et le pilote de base de données MySQL, qui vous aideront essentiellement à effectuer facilement des opérations sur une instance de base de données MySQL sans écrire beaucoup de code passe-partout. Les choses sont assez simples et simples avec Golang !

Au répertoire racine du projet Golang, ajoutez un autre dossier nommé database et créez un nouveau fichier nommé client.go

package database
import (
"jwt-authentication-golang/models"
"log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var Instance *gorm.DB
var dbError error
func Connect(connectionString string) () {
Instance, dbError = gorm.Open(mysql.Open(connectionString), &gorm.Config{})
if dbError != nil {
log.Fatal(dbError)
panic("Cannot connect to DB")
}
log.Println("Connected to Database!")
}
func Migrate() {
Instance.AutoMigrate(&models.User{})
log.Println("Database Migration Completed!")
}

Ligne 8 : Ici, nous définissons une instance de la base de données. Cette variable sera utilisée dans toute l'application pour communiquer avec la base de données.

Ligne 10-17 : La fonction Connect() prend la chaîne de connexion MySQL (que nous allons bientôt passer de la méthode principale) et tente de se connecter à la base de données à l'aide de GORM.

Ligne 18-21 : Une fois connecté à la base de données à l'aide de la fonction Connect() précédente, nous appellerons cette fonction Migrate() pour nous assurer que dans notre base de données, il existe une table d' utilisateurs . S'il n'est pas présent, GORM créera automatiquement une nouvelle table nommée « utilisateurs » pour nous.

Assurez-vous d'installer les packages golang en exécutant les commandes go get.

Accédez au fichier main.go et modifiez la fonction main() comme indiqué ci-dessous.

func main() {
// Initialize Database
database.Connect("root:root@tcp(localhost:3306)/jwt_demo?parseTime=true")
database.Migrate()
}

Comme indiqué précédemment, nous allons d'abord nous connecter à la base de données à l'aide de la chaîne de connexion fournie. Une fois cela fait, nous appliquerons les migrations.

Le codage en dur de la chaîne de connexion dans le code n'est évidemment pas un bon moyen d'y remédier. Pour garder l'article court, je l'ai fait. Pour savoir comment stocker la chaîne de connexion et d'autres variables dans un fichier JSON, lisez mon article précédent dans lequel j'ai utilisé Viper pour charger des configurations à partir d'un fichier JSON lors de l'exécution.

Une autre chose mystérieuse pour moi est que la chaîne de connexion à la base de données MySQL me posait des problèmes lorsque j'utilisais simplement ' root:root@tcp(localhost:3306)/jwt_demo '. C'était l'erreur que j'obtenais.

2022/04/24 18:39:06 D:/repos/golang/jwt-authentication-golang/controllers/tokencontroller.go:27 sql: Scan error on column index 1, name "created_at": unsupported Scan, storing driver.Value type []uint8 into type *time.Time

Après une recherche rapide, j'ai trouvé qu'il est obligatoire d'inclure le paramètre parseTime dans la chaîne de connexion pour que les choses fonctionnent. Voici la chaîne de connexion qui a fonctionné pour moi.

root:root@tcp(localhost:3306)/jwt_demo?parseTime=true

Assurez-vous que la base de données mentionnée est déjà créée sur votre serveur. Dans mon cas, j'ai dû créer une base de données/schéma nommé jwt_demo avant d'exécuter l'application. Sinon, GORM se plaindrait en disant qu'une telle base de données ne pouvait pas être trouvée sur le serveur.

Exécutons l'application à l'aide de la commande suivante.

go run .

On s'attend à ce que l'application crée une nouvelle table nommée users sur votre base de données.

image 54 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Voilà, c'est fait. Continuons avec les contrôleurs maintenant.

Contrôleur utilisateur - Enregistrement d'un nouvel utilisateur

Tout d'abord, écrivons un contrôleur et un point de terminaison chargés de créer de nouveaux utilisateurs et d'effectuer quelques vérifications de validation de base.

Mais avant cela, comme mentionné précédemment, ajoutons quelques méthodes d'assistance qui peuvent hacher et comparer les mots de passe. Ouvrez le fichier models/user.go et ajoutez-y ces deux méthodes. Notez que ces méthodes ont des récepteurs de type *User.

func (user *User) HashPassword(password string) error {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
if err != nil {
return err
}
user.Password = string(bytes)
return nil
}
func (user *User) CheckPassword(providedPassword string) error {
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(providedPassword))
if err != nil {
return err
}
return nil
}

Vous devrez exécuter la commande suivante sur votre terminal pour installer le package bcrypt utilisé pour chiffrer/déchiffrer les mots de passe.

go get golang.org/x/crypto/bcrypt

Ensuite, créez un nouveau dossier nommé controllers à la racine du projet et ajoutez un nouveau fichier sous celui-ci nommé usercontroller.go

package controllers
import (
"jwt-authentication-golang/database"
"jwt-authentication-golang/models"
"net/http"
"github.com/gin-gonic/gin"
)
func RegisterUser(context *gin.Context) {
var user models.User
if err := context.ShouldBindJSON(&user); err != nil {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
context.Abort()
return
}
if err := user.HashPassword(user.Password); err != nil {
context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
context.Abort()
return
}
record := database.Instance.Create(&user)
if record.Error != nil {
context.JSON(http.StatusInternalServerError, gin.H{"error": record.Error.Error()})
context.Abort()
return
}
context.JSON(http.StatusCreated, gin.H{"userId": user.ID, "email": user.Email, "username": user.Username})
}

Ligne 9 : Ici nous déclarons une variable locale de type models.User.

Ligne 10-14 : Tout ce qui est envoyé par le client en tant que corps JSON sera mappé dans la variable utilisateur. C'est assez simple avec du gin.

Lignes 15-19 : Ici, nous hachons le mot de passe à l'aide des assistants bcrypt que nous avons ajoutés précédemment au fichier models/user.go.

Ligne 20 : Une fois hachées, nous stockons les données utilisateur dans la base de données à l'aide de l'instance globale GORM que nous avons initialisée précédemment dans le fichier principal.

Lignes 21 à 25 : s'il y a une erreur lors de l'enregistrement des données, l'application émettra un code d'erreur de serveur interne HTTP 500 et abandonnera la demande.

Ligne 26 : Enfin, si tout se passe bien, nous renvoyons l'identifiant, le nom et l'e-mail de l'utilisateur au client avec un code d'état 200 SUCCESS.

Token Controller - Génération de JWT dans Golang

Puisque nous sommes prêts avec le point de terminaison de création d'utilisateur, ajoutons un autre point de terminaison qui peut générer des JWT pour nous. Il s'agit du cœur de l'implémentation de l'authentification JWT dans l'API REST Golang.

Tout d'abord, ajoutons quelques assistants pour générer le JWT réel et le valider.

package auth
import (
"errors"
"time"
"github.com/dgrijalva/jwt-go"
)
var jwtKey = []byte("supersecretkey")
type JWTClaim struct {
Username string `json:"username"`
Email string `json:"email"`
jwt.StandardClaims
}
func GenerateJWT(email string, username string) (tokenString string, err error) {
expirationTime := time.Now().Add(1 * time.Hour)
claims:= &JWTClaim{
Email: email,
Username: username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err = token.SignedString(jwtKey)
return
}
func ValidateToken(signedToken string) (err error) {
token, err := jwt.ParseWithClaims(
signedToken,
&JWTClaim{},
func(token *jwt.Token) (interface{}, error) {
return []byte(jwtKey), nil
},
)
if err != nil {
return
}
claims, ok := token.Claims.(*JWTClaim)
if !ok {
err = errors.New("couldn't parse claims")
return
}
if claims.ExpiresAt < time.Now().Local().Unix() {
err = errors.New("token expired")
return
}
return
}

Ligne 7 : Ici, nous déclarons une clé secrète qui sera utilisée plus tard pour générer des JWT. Pour l'instant, la clé est "supersecretkey". Idéalement, vous devriez stocker cette valeur en dehors du code. Mais par souci de simplicité, procédons tel quel.

Ligne 8-12 : Nous définissons une structure personnalisée pour les revendications JWT qui deviendra finalement la charge utile du JWT (si vous vous souvenez de la première section de cet article).

Ligne 13-15 : Dans la fonction GenerateJWT(), qui prend l'e-mail et le nom d'utilisateur comme paramètres, renverrait la chaîne JWT générée. Ici, nous définissons un délai d'expiration par défaut sur 1 heure, qui peut (et doit être) configuré. À partir de là, nous créons une nouvelle variable de réclamation avec les données disponibles et le délai d'expiration. Enfin, nous générons le jeton à l'aide de l'algorithme de signature HS256 en transmettant les revendications précédemment créées.

Lignes 26-47 : Ici, dans la fonction ValidateToken(), nous prendrions la chaîne de jeton provenant de l'en-tête de requête HTTP du client et la validerions. Plus loin dans ce didacticiel, nous utiliserons cette fonction dans notre middleware d'authentification pour vérifier si la demande client entrante est authentifiée. Clair, ouais? Donc, ici, nous allons essayer d'analyser le JWT en revendications en utilisant la méthode d'assistance du package JWT "ParseWithClaims". À partir du jeton analysé, nous extrayons les revendications à la ligne 37. À l'aide de ces revendications, nous vérifions à la ligne 42 si le jeton a réellement expiré ou non. C'est ça. Assez simple si vous comprenez le flux.

Maintenant que nos assistants ont terminé, commençons par écrire notre contrôleur Token.

Créez un autre fichier nommé tokencontroller. allez dans le dossier des contrôleurs.

package controllers
import (
"jwt-authentication-golang/auth"
"jwt-authentication-golang/database"
"jwt-authentication-golang/models"
"net/http"
"github.com/gin-gonic/gin"
)
type TokenRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
func GenerateToken(context *gin.Context) {
var request TokenRequest
var user models.User
if err := context.ShouldBindJSON(&request); err != nil {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
context.Abort()
return
}
// check if email exists and password is correct
record := database.Instance.Where("email = ?", request.Email).First(&user)
if record.Error != nil {
context.JSON(http.StatusInternalServerError, gin.H{"error": record.Error.Error()})
context.Abort()
return
}
credentialError := user.CheckPassword(request.Password)
if credentialError != nil {
context.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"})
context.Abort()
return
}
tokenString, err:= auth.GenerateJWT(user.Email, user.Username)
if err != nil {
context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
context.Abort()
return
}
context.JSON(http.StatusOK, gin.H{"token": tokenString})
}

Ligne 9-12 : Ici, nous définissons une structure simple qui correspondra essentiellement à ce que le point de terminaison attendrait en tant que corps de la requête. Celui-ci contiendrait l'identifiant de messagerie et le mot de passe de l'utilisateur.

Ligne 13-41 : Dans la fonction GenerateToken(), nous lions la requête entrante à la structure TokenRequest. Chez Line 22, nous communiquons avec la base de données via GORM pour vérifier si l'identifiant e-mail transmis par la requête existe réellement dans la base de données. Si tel est le cas, il récupérera le premier enregistrement correspondant. Sinon, un message d'erreur approprié sera renvoyé par le code. Ensuite, à la ligne 28, nous vérifions si le mot de passe saisi correspond à celui de la base de données. Pour cela, nous utiliserons la méthode CheckPassword() que nous avons créée précédemment dans le fichier jwt.go, vous vous souvenez ?

Si tout se passe bien et que le mot de passe correspond, nous passons à la ligne 34, où nous générons le JWT à l'aide de la fonction GenerateJWT(). Cela renverrait une chaîne de jeton signée avec une expiration d'une heure, qui à son tour sera renvoyée au client en réponse avec un code d'état 200.

Contrôleur sécurisé – Top Secret Pongs

Maintenant que nous avons configuré nos contrôleurs de jeton et d'utilisateur. Écrivons un endpoint qui contiendra des informations super-secrètes, qui seront un « pong », évidemment.

Créez un nouveau fichier de contrôleur sous le dossier des contrôleurs et nommez-le securecontroller.go

package controllers
import (
"net/http"
"github.com/gin-gonic/gin"
)
func Ping(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{"message": "pong"})
}

Trop simple, hein ? L'idée est de sécuriser ce point de terminaison afin que seules les requêtes ayant un JWT valide dans l'en-tête de la requête puissent y accéder. Et oui, il renvoie simplement un message pong avec un code d'état 200.

Alors, comment vérifier si la requête entrante contient un jeton valide ? N'oubliez pas que nous avons écrit une méthode d'assistance plus tôt pour lutter contre ce cas d'utilisation particulier. Une option consiste à faire le tour et à utiliser cette méthode dans chaque point de terminaison que nous devons sécuriser. Mais, s'il y a des dizaines de centaines de points de terminaison dans votre application, ce ne serait pas faisable, n'est-ce pas ? C'est exactement pourquoi il faut placer ce contrôle de validation quelque part dans le monde et le rendre utilisable par tous les terminaux que nous devons sécuriser.

L'intergiciel est la réponse à cela. Ainsi, ce que fait un middleware, c'est qu'il s'attache au pipeline HTTP de l'application. Ainsi, une fois qu'un client envoie une requête, le premier bloc qu'il atteint sera le middleware, seulement après cela, la requête atteindra le point de terminaison réel. C'est un endroit approprié pour positionner notre contrôle de validation de jeton, n'est-ce pas ? Voyons comment c'est fait.

Intergiciel d'authentification - Validation du jeton

Créez un nouveau dossier sous la racine du projet et nommez-le middlewares. Pour cet article, nous n'aurons besoin que d'un seul middleware, à savoir vérifier la validité du jeton entrant à partir de la requête client. Créons un nouveau fichier et nommons-le auth.go

package middlewares
import (
"jwt-authentication-golang/auth"
"github.com/gin-gonic/gin"
)
func Auth() gin.HandlerFunc{
return func(context *gin.Context) {
tokenString := context.GetHeader("Authorization")
if tokenString == "" {
context.JSON(401, gin.H{"error": "request does not contain an access token"})
context.Abort()
return
}
err:= auth.ValidateToken(tokenString)
if err != nil {
context.JSON(401, gin.H{"error": err.Error()})
context.Abort()
return
}
context.Next()
}
}

Ligne 8 : Extrait l'en-tête Authorization du contexte HTTP. Idéalement, nous nous attendons à ce que le jeton soit envoyé en tant qu'en-tête par le client. Si aucun jeton n'est trouvé dans l'en-tête, l'application génère une erreur 401 avec le message d'erreur approprié.

Ligne 14 : Ici, nous validons le jeton à l'aide de la fonction d'assistance créée précédemment. Si le jeton s'avère invalide ou expiré, l'application lèvera une exception 401 Unauthorized. Si le jeton est valide, le middleware autorise le flux et la demande atteint le point de terminaison du contrôleur requis. Aussi simple que cela.

Configuration des itinéraires

Maintenant que nous avons créé les terminaux et le middleware, comment connecter le tout ? C'est là que le routage est utile, en particulier le routage du gin est super génial à ce sujet.

Ouvrez le main.go et assurez-vous que votre code ressemble à l'extrait ci-dessous.

package main
import (
"jwt-authentication-golang/controllers"
"jwt-authentication-golang/database"
"jwt-authentication-golang/middlewares"
"github.com/gin-gonic/gin"
)
func main() {
// Initialize Database
database.Connect("root:root@tcp(localhost:3306)/jwt_demo?parseTime=true")
database.Migrate()
// Initialize Router
router := initRouter()
router.Run(":8080")
}
func initRouter() *gin.Engine {
router := gin.Default()
api := router.Group("/api")
{
api.POST("/token", controllers.GenerateToken)
api.POST("/user/register", controllers.RegisterUser)
secured := api.Group("/secured").Use(middlewares.Auth())
{
secured.GET("/ping", controllers.Ping)
}
}
return router
}

Outre les lignes de code existantes, nous avons ajouté une méthode initRouter() qui renvoie une variable de routeur gin.

Ligne 13 : Appelle la nouvelle fonction initRouter().

Ligne 17 : Crée une nouvelle instance de routeur Gin.

Ligne 18-26 : Voici une fonctionnalité que j'ai aimé apprendre. Imaginez que nous ayons besoin de quelques itinéraires comme ci-dessous.

  • api/utilisateur/s'inscrire
  • api/jeton
  • api/sécurisé/ping
  • api/sécurisé/autre chose

GIN facilite le regroupement efficace de ces éléments. À la ligne 18, nous avons tout regroupé sous /api. Ensuite, à la ligne 20, nous avons routé l'api/token vers la fonction GenerateToken que nous avons écrite dans le tokencontroller. De même, pour le point de terminaison d'enregistrement de l'utilisateur également.

Maintenant, nous devons sécuriser tous les points de terminaison qui relèveront des routes api/secured/. C'est ici que nous disons à GIN d'utiliser le middleware que nous avons créé. Dans Line 22, nous utilisons le middleware Auth qui sera attaché à cet ensemble particulier de points de terminaison. Cool, ouais? Permet d'économiser beaucoup de lignes de code.

Enfin, à la ligne 14, nous exécutons le serveur API sur le port 8080.

Test de l'API Golang avec le client REST VSCode

Comme mentionné précédemment, le principal avantage de l'utilisation de VS Code pour le développement rapide d'API est la possibilité de tester les points de terminaison de l'API directement depuis l'IDE. Ainsi, sous la racine du projet, j'ai créé un nouveau dossier nommé rest , où j'ai placé tous les fichiers .rest contenant des exemples de requêtes API. Nous allons l'utiliser pour tester notre implémentation d'authentification JWT dans Golang.

Tout d'abord, assurez-vous que vous avez installé l'extension REST Client sur votre VS Code.

image 58 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Assurez-vous que votre serveur de base de données est opérationnel. Démarrez votre serveur d'API Golang en exécutant la commande suivante dans le répertoire racine du projet.

go run .

Vous verriez quelque chose comme ça sur votre terminal.

image 55 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Enregistrez un nouvel utilisateur

Créez un nouveau dossier nommé rest et ajoutez un nouveau fichier nommé user.rest

@host = localhost:8080

// Register User
POST http://{{host}}/api/user/register HTTP/1.1
content-type: application/json

{
"name": "Mukesh Murugan",
"username": "mukesh.murugan",
"email": "mukesh@go.com",
"password": "123465789"
}

###

Donc, ce que nous faisons, c'est envoyer une requête POST au point de terminaison api/user/register avec un corps JSON qui définit le nom d'utilisateur, l'e-mail, le nom et le mot de passe de l'utilisateur dont nous avons besoin pour être enregistré dans l'application. Si votre client REST est correctement installé sur votre code VS, vous verrez une option d'envoi de demande au-dessus de la ligne 4. Cliquez dessus !

image 56 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Vous pouvez voir que l'API répond avec l'ID utilisateur, le nom d'utilisateur et l'e-mail avec un code d'état 201. Cela signifie que notre nouvel utilisateur a été enregistré avec succès dans la base de données.

De plus, vous voyez un joli journal sur le terminal de GIN indiquant la demande que nous venons d'envoyer.

image 59 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Connectons-nous à notre MySQL Workbench pour vérifier si l'utilisateur est ajouté à la base de données.

image 57 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Voilà, tout va bien.

Générer un jeton Web JSON

Maintenant que nous avons enregistré l'utilisateur, utilisons ses informations d'identification pour générer de nouveaux JWT.

Créez un nouveau fichier dans le même dossier rest et nommez-le token.rest

@host = localhost:8080

// Generate JWT
POST http://{{host}}/api/token HTTP/1.1
content-type: application/json

{
"email": "mukesh@go.com",
"password": "123465789"
}

###

Ici, nous allons simplement transmettre les informations d'identification de l'utilisateur au point de terminaison api/token. C'est ce qu'un client fera pour générer des jetons d'authentification.

Envoyez la demande. Vous pouvez voir que l'API répond avec un jeton JWT réel.

image 60 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Faisons encore une chose pour le plaisir. Copiez ce jeton et rendez-vous sur jwt.io

Collez le jeton dans la zone de texte Encodé.

image 61 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Ici, vous pouvez voir que la charge utile contient le nom d'utilisateur et l'e-mail de notre utilisateur. Super cool, ouais?

Gardez ce jeton de côté. Nous en aurons besoin lors de notre prochain test, où nous enverrons une requête au point de terminaison api/secured/ping qui se trouve être sécurisé. Le middleware que nous avons créé n'autorisera l'accès que si la demande a un JWT valide dans l'en-tête d'autorisation.

Point de terminaison sécurisé – Middleware Magic

Créez un nouveau fichier nommé secure.rest

@host = localhost:8080

// Access a Secured API Endpoint
GET http://{{host}}/api/secured/ping HTTP/1.1
content-type: application/json
authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im11a2VzaC5tdXJ1Z2FuIiwiZW1haWwiOiJtdWtlc2hAZ28uY29tIiwiZXhwIjoxNjUwNzQzMjA1fQ.7cAcWxvpqJ1DDZ-ZOM2kIKKedeCEWuUzl0Hj2VuMxYA

###

Ici, à la ligne 6, vous pouvez voir que nous avons mentionné l'en-tête d'autorisation et collé un JWT factice. Nous savons donc que ce JWT n'est pas valide. Testons-le.

image 62 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Là, l'application indique que ce JWT particulier a déjà expiré, il y a environ 20 heures. Maintenant, dans notre requête, remplaçons le JWT par celui que nous avons généré précédemment. N'oubliez pas que le JWT n'est valide que pendant 1 heure à partir du moment de la génération. Une fois que vous avez ajouté le nouveau JWT à l'en-tête de la demande, envoyez la demande.

image 63 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Super, nous obtenons la réponse ping du point de terminaison sécurisé.

C'est la fin de cet article détaillé sur l'authentification JWT dans l'API Golang REST !

Sommaire

Nous avons appris en détail comment implémenter facilement l'authentification JWT dans les API Golang Rest. En cours de route, nous avons couvert divers sujets tels que les bases de JWT, les premiers pas avec GIN Framework, la configuration de GORM et les migrations MySQL, l'enregistrement des utilisateurs, la génération de jetons à l'aide du package JWT-GO, l'utilisation des middlewares GIN, le hachage et le décryptage des mots de passe à l'aide du package bcrypt, travailler avec Gin Routes et ainsi de suite.

Partagez cet article avec vos collègues et cercles de développement si vous avez trouvé cela intéressant. Vous pouvez trouver le  code source de cette implémentation mentionnée ici . Acclamations! 

 Source : https://codewithmukesh.com/blog/jwt-authentication-in-golang/

#golang #restapi #jwt 

What is GEEK

Buddha Community

Mise En œuvre De L'authentification JWT Dans Les API Golang REST
Wilford  Pagac

Wilford Pagac

1594289280

What is REST API? An Overview | Liquid Web

What is REST?

The REST acronym is defined as a “REpresentational State Transfer” and is designed to take advantage of existing HTTP protocols when used for Web APIs. It is very flexible in that it is not tied to resources or methods and has the ability to handle different calls and data formats. Because REST API is not constrained to an XML format like SOAP, it can return multiple other formats depending on what is needed. If a service adheres to this style, it is considered a “RESTful” application. REST allows components to access and manage functions within another application.

REST was initially defined in a dissertation by Roy Fielding’s twenty years ago. He proposed these standards as an alternative to SOAP (The Simple Object Access Protocol is a simple standard for accessing objects and exchanging structured messages within a distributed computing environment). REST (or RESTful) defines the general rules used to regulate the interactions between web apps utilizing the HTTP protocol for CRUD (create, retrieve, update, delete) operations.

What is an API?

An API (or Application Programming Interface) provides a method of interaction between two systems.

What is a RESTful API?

A RESTful API (or application program interface) uses HTTP requests to GET, PUT, POST, and DELETE data following the REST standards. This allows two pieces of software to communicate with each other. In essence, REST API is a set of remote calls using standard methods to return data in a specific format.

The systems that interact in this manner can be very different. Each app may use a unique programming language, operating system, database, etc. So, how do we create a system that can easily communicate and understand other apps?? This is where the Rest API is used as an interaction system.

When using a RESTful API, we should determine in advance what resources we want to expose to the outside world. Typically, the RESTful API service is implemented, keeping the following ideas in mind:

  • Format: There should be no restrictions on the data exchange format
  • Implementation: REST is based entirely on HTTP
  • Service Definition: Because REST is very flexible, API can be modified to ensure the application understands the request/response format.
  • The RESTful API focuses on resources and how efficiently you perform operations with it using HTTP.

The features of the REST API design style state:

  • Each entity must have a unique identifier.
  • Standard methods should be used to read and modify data.
  • It should provide support for different types of resources.
  • The interactions should be stateless.

For REST to fit this model, we must adhere to the following rules:

  • Client-Server Architecture: The interface is separate from the server-side data repository. This affords flexibility and the development of components independently of each other.
  • Detachment: The client connections are not stored on the server between requests.
  • Cacheability: It must be explicitly stated whether the client can store responses.
  • Multi-level: The API should work whether it interacts directly with a server or through an additional layer, like a load balancer.

#tutorials #api #application #application programming interface #crud #http #json #programming #protocols #representational state transfer #rest #rest api #rest api graphql #rest api json #rest api xml #restful #soap #xml #yaml

An API-First Approach For Designing Restful APIs | Hacker Noon

I’ve been working with Restful APIs for some time now and one thing that I love to do is to talk about APIs.

So, today I will show you how to build an API using the API-First approach and Design First with OpenAPI Specification.

First thing first, if you don’t know what’s an API-First approach means, it would be nice you stop reading this and check the blog post that I wrote to the Farfetchs blog where I explain everything that you need to know to start an API using API-First.

Preparing the ground

Before you get your hands dirty, let’s prepare the ground and understand the use case that will be developed.

Tools

If you desire to reproduce the examples that will be shown here, you will need some of those items below.

  • NodeJS
  • OpenAPI Specification
  • Text Editor (I’ll use VSCode)
  • Command Line

Use Case

To keep easy to understand, let’s use the Todo List App, it is a very common concept beyond the software development community.

#api #rest-api #openai #api-first-development #api-design #apis #restful-apis #restful-api

Lets Cms

Lets Cms

1652251528

Opencart REST API extensions - V3.x | Rest API Integration, Affiliate

Opencart REST API extensions - V3.x | Rest API Integration : OpenCart APIs is fully integrated with the OpenCart REST API. This is interact with your OpenCart site by sending and receiving data as JSON (JavaScript Object Notation) objects. Using the OpenCart REST API you can register the customers and purchasing the products and it provides data access to the content of OpenCart users like which is publicly accessible via the REST API. This APIs also provide the E-commerce Mobile Apps.

Opencart REST API 
OCRESTAPI Module allows the customer purchasing product from the website it just like E-commerce APIs its also available mobile version APIs.

Opencart Rest APIs List 
Customer Registration GET APIs.
Customer Registration POST APIs.
Customer Login GET APIs.
Customer Login POST APIs.
Checkout Confirm GET APIs.
Checkout Confirm POST APIs.


If you want to know Opencart REST API Any information, you can contact us at -
Skype: jks0586,
Email: letscmsdev@gmail.com,
Website: www.letscms.com, www.mlmtrees.com
Call/WhatsApp/WeChat: +91–9717478599.

Download : https://www.opencart.com/index.php?route=marketplace/extension/info&extension_id=43174&filter_search=ocrest%20api
View Documentation : https://www.letscms.com/documents/api/opencart-rest-api.html
More Information : https://www.letscms.com/blog/Rest-API-Opencart
VEDIO : https://vimeo.com/682154292  

#opencart_api_for_android #Opencart_rest_admin_api #opencart_rest_api #Rest_API_Integration #oc_rest_api #rest_api_ecommerce #rest_api_mobile #rest_api_opencart #rest_api_github #rest_api_documentation #opencart_rest_admin_api #rest_api_for_opencart_mobile_app #opencart_shopping_cart_rest_api #opencart_json_api

Lets Cms

Lets Cms

1652251629

Unilevel MLM Wordpress Rest API FrontEnd | UMW Rest API Woocommerce

Unilevel MLM Wordpress Rest API FrontEnd | UMW Rest API Woocommerce Price USA, Philippines : Our API’s handle the Unilevel MLM woo-commerce end user all functionalities like customer login/register. You can request any type of information which is listed below, our API will provide you managed results for your all frontend needs, which will be useful for your applications like Mobile App etc.
Business to Customer REST API for Unilevel MLM Woo-Commerce will empower your Woo-commerce site with the most powerful Unilevel MLM Woo-Commerce REST API, you will be able to get and send data to your marketplace from other mobile apps or websites using HTTP Rest API request.
Our plugin is used JWT authentication for the authorization process.

REST API Unilevel MLM Woo-commerce plugin contains following APIs.
User Login Rest API
User Register Rest API
User Join Rest API
Get User info Rest API
Get Affiliate URL Rest API 
Get Downlines list Rest API
Get Bank Details Rest API
Save Bank Details Rest API
Get Genealogy JSON Rest API
Get Total Earning Rest API
Get Current Balance Rest API
Get Payout Details Rest API
Get Payout List Rest API
Get Commissions List Rest API
Withdrawal Request Rest API
Get Withdrawal List Rest API

If you want to know more information and any queries regarding Unilevel MLM Rest API Woocommerce WordPress Plugin, you can contact our experts through 
Skype: jks0586, 
Mail: letscmsdev@gmail.com,
Website: www.letscms.com, www.mlmtrees.com,
Call/WhatsApp/WeChat: +91-9717478599.  

more information : https://www.mlmtrees.com/product/unilevel-mlm-woocommerce-rest-api-addon

Visit Documentation : https://letscms.com/documents/umw_apis/umw-apis-addon-documentation.html

#Unilevel_MLM_WooCommerce_Rest_API's_Addon #umw_mlm_rest_api #rest_api_woocommerce_unilevel #rest_api_in_woocommerce #rest_api_woocommerce #rest_api_woocommerce_documentation #rest_api_woocommerce_php #api_rest_de_woocommerce #woocommerce_rest_api_in_android #woocommerce_rest_api_in_wordpress #Rest_API_Woocommerce_unilevel_mlm #wp_rest_api_woocommerce

Anne  de Morel

Anne de Morel

1656762060

Mise En œuvre De L'authentification JWT Dans Les API Golang REST

Dans cet article, nous allons apprendre à implémenter l'authentification JWT dans les API Golang REST et à la sécuriser avec le middleware d'authentification. Nous allons construire une API REST Golang simple mais bien organisée avec des packages tels que Gin pour le routage (principalement), GORM pour la persistance des données utilisateur dans une base de données MySQL, etc. Nous allons également créer un middleware qui peut sécuriser les points de terminaison et autoriser les demandes qui ont un JWT valide dans l'en-tête d'autorisation de la demande.

Dans un article précédent, nous avons appris comment implémenter CRUD dans l'API Golang REST avec Mux & GORM. C'est une bonne référence pour démarrer avec les API dans Golang. Lire ici .

Vous pouvez trouver le code source complet de l'authentification JWT dans l'implémentation Golang ici .

JWT expliqué

Donc, avant de commencer l'implémentation, qu'est-ce qu'un JWT ou un jeton Web JSON ?

Vous pouvez ignorer cette section si vous savez déjà ce qu'est un JWT et ce qu'il fait.

Ces jetons sont utilisés par les API RESTful pour l'authentification et l'échange d'informations lorsqu'il n'est pas nécessaire de gérer l'état de l'application.

Selon jwt.io ,

Les jetons Web JSON sont une méthode RFC 7519 ouverte et standard de l'industrie   pour représenter les revendications en toute sécurité entre deux parties.

JWT est un très bon concurrent pour sécuriser les API REST. Voyons la structure d'un JWT réel en action. Rendez-vous sur jwt.io où il y a un exemple de JWT pour notre compréhension.

image 53 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Vous pouvez voir que ces jetons sont séparés en 3 parties avec un point.

  1. Entête
  2. Charge utile
  3. Signature

L'en-tête contient l'algorithme de signature utilisé tel que RSA ou HMAC SHA256. La charge utile contient les données à échanger. Il s'agit généralement de réclamations envoyées par le serveur, telles que le nom d'utilisateur, l'e-mail, etc. Notez que les données sensibles telles que les mots de passe ne doivent jamais être envoyées via un jeton Web JSON. La troisième partie du jeton, qui est la signature, est utilisée pour vérifier que le JWT n'a pas été falsifié.

Premiers pas avec l'authentification JWT dans Golang

Commençons par implémenter l'authentification JWT dans l'API Golang REST. Visual Code sera l'IDE de choix pour l'article (et probablement tous les autres articles Golang que je publierai à l'avenir), en raison de sa facilité d'utilisation et de sa productivité. Assurez-vous d'avoir le dernier SDK stable de Golang. La dernière version disponible lors de la rédaction de cet article est Go 1.18.1.

Assurez-vous également que vous avez installé l'extension Golang sur VS Code, ce qui aide énormément à améliorer l'expérience de développement de Golang. Une autre raison vraiment intéressante d'utiliser VSCode pour le développement d'API est la possibilité d'envoyer des requêtes à l'API directement depuis l'interface VS Code en utilisant les conventions de l'API REST. Permet de gagner beaucoup de temps, plutôt que de passer au facteur ou à d'autres clients REST.

Ouvrez un nouveau dossier dans VS Code et entrez ce qui suit pour initialiser les dépendances du projet Golang.

go mod init jwt-authentication-golang

Ici, créez un fichier main.go avec le code passe-partout suivant.

package main
func main() {
}

Qu'allons-nous construire ?

Permettez-moi de donner un bref aperçu de ce que nous allons construire. Il s'agira donc d'une API REST avec les contrôleurs suivants.

  1. Contrôleur de jetons – Celui-ci aura un point de terminaison qui sera utilisé pour générer les JWT. Ici, l'utilisateur doit envoyer une liste d'e-mails/mots de passe valides.
  2. Contrôleur d'utilisateur - Il y aura un point de terminaison "enregistrer l'utilisateur" qui peut être utilisé pour créer de nouveaux utilisateurs.
  3. Contrôleur sécurisé - Un contrôleur factice qui sera sécurisé par l'authentification JWT. Ceci est juste pour montrer la capacité du middleware que nous allons construire pour restreindre l'accès aux seules demandes qui ont un JWT valide dans l'en-tête de la demande.

Nous ajouterons également des assistants pour JWT qui nous aideront à générer les jetons avec des délais d'expiration et des revendications appropriés, ainsi qu'un moyen de valider les jetons envoyés. Ceci sera utilisé par notre middleware personnalisé pour restreindre l'accès. De plus, comme mentionné précédemment, lors du processus d'inscription, nous stockerons les données de l'utilisateur dans une base de données MySQL à l'aide de l'abstraction GORM. Ici, nous utiliserons un groupe d'assistants pour chiffrer/hacher les mots de passe des utilisateurs. Nous ne voulons pas stocker le mot de passe réel directement dans la base de données, n'est-ce pas ?

Gin – Introduction rapide

Je suis tombé sur Gin, qui est un framework de développement Web pour les API Golang. Ils s'annoncent 40 fois plus rapides que les routeurs HTTP normaux. Ils sont également assez populaires, avec plus de 55 000 étoiles sur Github. J'ai aimé leur outillage et l'amélioration de l'expérience de développement. Gin est un framework qui réduit le code passe-partout qui entrerait normalement dans la construction de ces applications. Exécutez ce qui suit pour installer gin sur votre machine et utilisez-le pour des projets golang.

go get -u github.com/gin-gonic/gin

Pour cet article, nous utiliserons les routeurs Gin et l'implémentation du middleware. Dans les prochains articles, nous explorerons davantage le framework Gin.

Configuration de la base de données et migrations

Tout d'abord, créons un dossier nommé models et un nouveau fichier pour le modèle utilisateur. Appelons-le user.go

type User struct {
gorm.Model
Name string `json:"name"`
Username string `json:"username" gorm:"unique"`
Email string `json:"email" gorm:"unique"`
Password string `json:"password"`
}

Comme vous pouvez le voir, notre modèle d'utilisateur aura un nom, un nom d'utilisateur, un e-mail et un mot de passe. Ici, le nom d'utilisateur et l'e-mail seront uniques. Cela signifie qu'une fois que nous aurons terminé notre application et essayé d'enregistrer de nouveaux utilisateurs avec le même nom d'utilisateur ou e-mail, le code ne vous permettra pas de le faire. La meilleure partie est que vous n'avez pas à écrire de code spécifiquement pour cela. Tout est géré par GORM.

La spécification gorm.Model ajoute certaines propriétés par défaut au modèle, comme l'identifiant, la date de création, la date de modification et la date de suppression.

Notez que, plus tard dans l'article, nous ajouterons quelques assistants à ce fichier go pour nous aider dans le hachage et la validation du mot de passe à l'aide d'un package de chiffrement de golang.

Ensuite, configurons le client qui nous aide à nous connecter à la base de données MySQL. Je suppose que vous avez déjà une instance MySQL en cours d'exécution sur votre ordinateur local au port 3306, qui est le port MySQL par défaut.

Installons les packages Golang requis. Exécutez les commandes suivantes.

go get gorm.io/gorm
go get gorm.io/driver/mysql

Cela installera les packages GORM et le pilote de base de données MySQL, qui vous aideront essentiellement à effectuer facilement des opérations sur une instance de base de données MySQL sans écrire beaucoup de code passe-partout. Les choses sont assez simples et simples avec Golang !

Au répertoire racine du projet Golang, ajoutez un autre dossier nommé database et créez un nouveau fichier nommé client.go

package database
import (
"jwt-authentication-golang/models"
"log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var Instance *gorm.DB
var dbError error
func Connect(connectionString string) () {
Instance, dbError = gorm.Open(mysql.Open(connectionString), &gorm.Config{})
if dbError != nil {
log.Fatal(dbError)
panic("Cannot connect to DB")
}
log.Println("Connected to Database!")
}
func Migrate() {
Instance.AutoMigrate(&models.User{})
log.Println("Database Migration Completed!")
}

Ligne 8 : Ici, nous définissons une instance de la base de données. Cette variable sera utilisée dans toute l'application pour communiquer avec la base de données.

Ligne 10-17 : La fonction Connect() prend la chaîne de connexion MySQL (que nous allons bientôt passer de la méthode principale) et tente de se connecter à la base de données à l'aide de GORM.

Ligne 18-21 : Une fois connecté à la base de données à l'aide de la fonction Connect() précédente, nous appellerons cette fonction Migrate() pour nous assurer que dans notre base de données, il existe une table d' utilisateurs . S'il n'est pas présent, GORM créera automatiquement une nouvelle table nommée « utilisateurs » pour nous.

Assurez-vous d'installer les packages golang en exécutant les commandes go get.

Accédez au fichier main.go et modifiez la fonction main() comme indiqué ci-dessous.

func main() {
// Initialize Database
database.Connect("root:root@tcp(localhost:3306)/jwt_demo?parseTime=true")
database.Migrate()
}

Comme indiqué précédemment, nous allons d'abord nous connecter à la base de données à l'aide de la chaîne de connexion fournie. Une fois cela fait, nous appliquerons les migrations.

Le codage en dur de la chaîne de connexion dans le code n'est évidemment pas un bon moyen d'y remédier. Pour garder l'article court, je l'ai fait. Pour savoir comment stocker la chaîne de connexion et d'autres variables dans un fichier JSON, lisez mon article précédent dans lequel j'ai utilisé Viper pour charger des configurations à partir d'un fichier JSON lors de l'exécution.

Une autre chose mystérieuse pour moi est que la chaîne de connexion à la base de données MySQL me posait des problèmes lorsque j'utilisais simplement ' root:root@tcp(localhost:3306)/jwt_demo '. C'était l'erreur que j'obtenais.

2022/04/24 18:39:06 D:/repos/golang/jwt-authentication-golang/controllers/tokencontroller.go:27 sql: Scan error on column index 1, name "created_at": unsupported Scan, storing driver.Value type []uint8 into type *time.Time

Après une recherche rapide, j'ai trouvé qu'il est obligatoire d'inclure le paramètre parseTime dans la chaîne de connexion pour que les choses fonctionnent. Voici la chaîne de connexion qui a fonctionné pour moi.

root:root@tcp(localhost:3306)/jwt_demo?parseTime=true

Assurez-vous que la base de données mentionnée est déjà créée sur votre serveur. Dans mon cas, j'ai dû créer une base de données/schéma nommé jwt_demo avant d'exécuter l'application. Sinon, GORM se plaindrait en disant qu'une telle base de données ne pouvait pas être trouvée sur le serveur.

Exécutons l'application à l'aide de la commande suivante.

go run .

On s'attend à ce que l'application crée une nouvelle table nommée users sur votre base de données.

image 54 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Voilà, c'est fait. Continuons avec les contrôleurs maintenant.

Contrôleur utilisateur - Enregistrement d'un nouvel utilisateur

Tout d'abord, écrivons un contrôleur et un point de terminaison chargés de créer de nouveaux utilisateurs et d'effectuer quelques vérifications de validation de base.

Mais avant cela, comme mentionné précédemment, ajoutons quelques méthodes d'assistance qui peuvent hacher et comparer les mots de passe. Ouvrez le fichier models/user.go et ajoutez-y ces deux méthodes. Notez que ces méthodes ont des récepteurs de type *User.

func (user *User) HashPassword(password string) error {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
if err != nil {
return err
}
user.Password = string(bytes)
return nil
}
func (user *User) CheckPassword(providedPassword string) error {
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(providedPassword))
if err != nil {
return err
}
return nil
}

Vous devrez exécuter la commande suivante sur votre terminal pour installer le package bcrypt utilisé pour chiffrer/déchiffrer les mots de passe.

go get golang.org/x/crypto/bcrypt

Ensuite, créez un nouveau dossier nommé controllers à la racine du projet et ajoutez un nouveau fichier sous celui-ci nommé usercontroller.go

package controllers
import (
"jwt-authentication-golang/database"
"jwt-authentication-golang/models"
"net/http"
"github.com/gin-gonic/gin"
)
func RegisterUser(context *gin.Context) {
var user models.User
if err := context.ShouldBindJSON(&user); err != nil {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
context.Abort()
return
}
if err := user.HashPassword(user.Password); err != nil {
context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
context.Abort()
return
}
record := database.Instance.Create(&user)
if record.Error != nil {
context.JSON(http.StatusInternalServerError, gin.H{"error": record.Error.Error()})
context.Abort()
return
}
context.JSON(http.StatusCreated, gin.H{"userId": user.ID, "email": user.Email, "username": user.Username})
}

Ligne 9 : Ici nous déclarons une variable locale de type models.User.

Ligne 10-14 : Tout ce qui est envoyé par le client en tant que corps JSON sera mappé dans la variable utilisateur. C'est assez simple avec du gin.

Lignes 15-19 : Ici, nous hachons le mot de passe à l'aide des assistants bcrypt que nous avons ajoutés précédemment au fichier models/user.go.

Ligne 20 : Une fois hachées, nous stockons les données utilisateur dans la base de données à l'aide de l'instance globale GORM que nous avons initialisée précédemment dans le fichier principal.

Lignes 21 à 25 : s'il y a une erreur lors de l'enregistrement des données, l'application émettra un code d'erreur de serveur interne HTTP 500 et abandonnera la demande.

Ligne 26 : Enfin, si tout se passe bien, nous renvoyons l'identifiant, le nom et l'e-mail de l'utilisateur au client avec un code d'état 200 SUCCESS.

Token Controller - Génération de JWT dans Golang

Puisque nous sommes prêts avec le point de terminaison de création d'utilisateur, ajoutons un autre point de terminaison qui peut générer des JWT pour nous. Il s'agit du cœur de l'implémentation de l'authentification JWT dans l'API REST Golang.

Tout d'abord, ajoutons quelques assistants pour générer le JWT réel et le valider.

package auth
import (
"errors"
"time"
"github.com/dgrijalva/jwt-go"
)
var jwtKey = []byte("supersecretkey")
type JWTClaim struct {
Username string `json:"username"`
Email string `json:"email"`
jwt.StandardClaims
}
func GenerateJWT(email string, username string) (tokenString string, err error) {
expirationTime := time.Now().Add(1 * time.Hour)
claims:= &JWTClaim{
Email: email,
Username: username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err = token.SignedString(jwtKey)
return
}
func ValidateToken(signedToken string) (err error) {
token, err := jwt.ParseWithClaims(
signedToken,
&JWTClaim{},
func(token *jwt.Token) (interface{}, error) {
return []byte(jwtKey), nil
},
)
if err != nil {
return
}
claims, ok := token.Claims.(*JWTClaim)
if !ok {
err = errors.New("couldn't parse claims")
return
}
if claims.ExpiresAt < time.Now().Local().Unix() {
err = errors.New("token expired")
return
}
return
}

Ligne 7 : Ici, nous déclarons une clé secrète qui sera utilisée plus tard pour générer des JWT. Pour l'instant, la clé est "supersecretkey". Idéalement, vous devriez stocker cette valeur en dehors du code. Mais par souci de simplicité, procédons tel quel.

Ligne 8-12 : Nous définissons une structure personnalisée pour les revendications JWT qui deviendra finalement la charge utile du JWT (si vous vous souvenez de la première section de cet article).

Ligne 13-15 : Dans la fonction GenerateJWT(), qui prend l'e-mail et le nom d'utilisateur comme paramètres, renverrait la chaîne JWT générée. Ici, nous définissons un délai d'expiration par défaut sur 1 heure, qui peut (et doit être) configuré. À partir de là, nous créons une nouvelle variable de réclamation avec les données disponibles et le délai d'expiration. Enfin, nous générons le jeton à l'aide de l'algorithme de signature HS256 en transmettant les revendications précédemment créées.

Lignes 26-47 : Ici, dans la fonction ValidateToken(), nous prendrions la chaîne de jeton provenant de l'en-tête de requête HTTP du client et la validerions. Plus loin dans ce didacticiel, nous utiliserons cette fonction dans notre middleware d'authentification pour vérifier si la demande client entrante est authentifiée. Clair, ouais? Donc, ici, nous allons essayer d'analyser le JWT en revendications en utilisant la méthode d'assistance du package JWT "ParseWithClaims". À partir du jeton analysé, nous extrayons les revendications à la ligne 37. À l'aide de ces revendications, nous vérifions à la ligne 42 si le jeton a réellement expiré ou non. C'est ça. Assez simple si vous comprenez le flux.

Maintenant que nos assistants ont terminé, commençons par écrire notre contrôleur Token.

Créez un autre fichier nommé tokencontroller. allez dans le dossier des contrôleurs.

package controllers
import (
"jwt-authentication-golang/auth"
"jwt-authentication-golang/database"
"jwt-authentication-golang/models"
"net/http"
"github.com/gin-gonic/gin"
)
type TokenRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
func GenerateToken(context *gin.Context) {
var request TokenRequest
var user models.User
if err := context.ShouldBindJSON(&request); err != nil {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
context.Abort()
return
}
// check if email exists and password is correct
record := database.Instance.Where("email = ?", request.Email).First(&user)
if record.Error != nil {
context.JSON(http.StatusInternalServerError, gin.H{"error": record.Error.Error()})
context.Abort()
return
}
credentialError := user.CheckPassword(request.Password)
if credentialError != nil {
context.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"})
context.Abort()
return
}
tokenString, err:= auth.GenerateJWT(user.Email, user.Username)
if err != nil {
context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
context.Abort()
return
}
context.JSON(http.StatusOK, gin.H{"token": tokenString})
}

Ligne 9-12 : Ici, nous définissons une structure simple qui correspondra essentiellement à ce que le point de terminaison attendrait en tant que corps de la requête. Celui-ci contiendrait l'identifiant de messagerie et le mot de passe de l'utilisateur.

Ligne 13-41 : Dans la fonction GenerateToken(), nous lions la requête entrante à la structure TokenRequest. Chez Line 22, nous communiquons avec la base de données via GORM pour vérifier si l'identifiant e-mail transmis par la requête existe réellement dans la base de données. Si tel est le cas, il récupérera le premier enregistrement correspondant. Sinon, un message d'erreur approprié sera renvoyé par le code. Ensuite, à la ligne 28, nous vérifions si le mot de passe saisi correspond à celui de la base de données. Pour cela, nous utiliserons la méthode CheckPassword() que nous avons créée précédemment dans le fichier jwt.go, vous vous souvenez ?

Si tout se passe bien et que le mot de passe correspond, nous passons à la ligne 34, où nous générons le JWT à l'aide de la fonction GenerateJWT(). Cela renverrait une chaîne de jeton signée avec une expiration d'une heure, qui à son tour sera renvoyée au client en réponse avec un code d'état 200.

Contrôleur sécurisé – Top Secret Pongs

Maintenant que nous avons configuré nos contrôleurs de jeton et d'utilisateur. Écrivons un endpoint qui contiendra des informations super-secrètes, qui seront un « pong », évidemment.

Créez un nouveau fichier de contrôleur sous le dossier des contrôleurs et nommez-le securecontroller.go

package controllers
import (
"net/http"
"github.com/gin-gonic/gin"
)
func Ping(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{"message": "pong"})
}

Trop simple, hein ? L'idée est de sécuriser ce point de terminaison afin que seules les requêtes ayant un JWT valide dans l'en-tête de la requête puissent y accéder. Et oui, il renvoie simplement un message pong avec un code d'état 200.

Alors, comment vérifier si la requête entrante contient un jeton valide ? N'oubliez pas que nous avons écrit une méthode d'assistance plus tôt pour lutter contre ce cas d'utilisation particulier. Une option consiste à faire le tour et à utiliser cette méthode dans chaque point de terminaison que nous devons sécuriser. Mais, s'il y a des dizaines de centaines de points de terminaison dans votre application, ce ne serait pas faisable, n'est-ce pas ? C'est exactement pourquoi il faut placer ce contrôle de validation quelque part dans le monde et le rendre utilisable par tous les terminaux que nous devons sécuriser.

L'intergiciel est la réponse à cela. Ainsi, ce que fait un middleware, c'est qu'il s'attache au pipeline HTTP de l'application. Ainsi, une fois qu'un client envoie une requête, le premier bloc qu'il atteint sera le middleware, seulement après cela, la requête atteindra le point de terminaison réel. C'est un endroit approprié pour positionner notre contrôle de validation de jeton, n'est-ce pas ? Voyons comment c'est fait.

Intergiciel d'authentification - Validation du jeton

Créez un nouveau dossier sous la racine du projet et nommez-le middlewares. Pour cet article, nous n'aurons besoin que d'un seul middleware, à savoir vérifier la validité du jeton entrant à partir de la requête client. Créons un nouveau fichier et nommons-le auth.go

package middlewares
import (
"jwt-authentication-golang/auth"
"github.com/gin-gonic/gin"
)
func Auth() gin.HandlerFunc{
return func(context *gin.Context) {
tokenString := context.GetHeader("Authorization")
if tokenString == "" {
context.JSON(401, gin.H{"error": "request does not contain an access token"})
context.Abort()
return
}
err:= auth.ValidateToken(tokenString)
if err != nil {
context.JSON(401, gin.H{"error": err.Error()})
context.Abort()
return
}
context.Next()
}
}

Ligne 8 : Extrait l'en-tête Authorization du contexte HTTP. Idéalement, nous nous attendons à ce que le jeton soit envoyé en tant qu'en-tête par le client. Si aucun jeton n'est trouvé dans l'en-tête, l'application génère une erreur 401 avec le message d'erreur approprié.

Ligne 14 : Ici, nous validons le jeton à l'aide de la fonction d'assistance créée précédemment. Si le jeton s'avère invalide ou expiré, l'application lèvera une exception 401 Unauthorized. Si le jeton est valide, le middleware autorise le flux et la demande atteint le point de terminaison du contrôleur requis. Aussi simple que cela.

Configuration des itinéraires

Maintenant que nous avons créé les terminaux et le middleware, comment connecter le tout ? C'est là que le routage est utile, en particulier le routage du gin est super génial à ce sujet.

Ouvrez le main.go et assurez-vous que votre code ressemble à l'extrait ci-dessous.

package main
import (
"jwt-authentication-golang/controllers"
"jwt-authentication-golang/database"
"jwt-authentication-golang/middlewares"
"github.com/gin-gonic/gin"
)
func main() {
// Initialize Database
database.Connect("root:root@tcp(localhost:3306)/jwt_demo?parseTime=true")
database.Migrate()
// Initialize Router
router := initRouter()
router.Run(":8080")
}
func initRouter() *gin.Engine {
router := gin.Default()
api := router.Group("/api")
{
api.POST("/token", controllers.GenerateToken)
api.POST("/user/register", controllers.RegisterUser)
secured := api.Group("/secured").Use(middlewares.Auth())
{
secured.GET("/ping", controllers.Ping)
}
}
return router
}

Outre les lignes de code existantes, nous avons ajouté une méthode initRouter() qui renvoie une variable de routeur gin.

Ligne 13 : Appelle la nouvelle fonction initRouter().

Ligne 17 : Crée une nouvelle instance de routeur Gin.

Ligne 18-26 : Voici une fonctionnalité que j'ai aimé apprendre. Imaginez que nous ayons besoin de quelques itinéraires comme ci-dessous.

  • api/utilisateur/s'inscrire
  • api/jeton
  • api/sécurisé/ping
  • api/sécurisé/autre chose

GIN facilite le regroupement efficace de ces éléments. À la ligne 18, nous avons tout regroupé sous /api. Ensuite, à la ligne 20, nous avons routé l'api/token vers la fonction GenerateToken que nous avons écrite dans le tokencontroller. De même, pour le point de terminaison d'enregistrement de l'utilisateur également.

Maintenant, nous devons sécuriser tous les points de terminaison qui relèveront des routes api/secured/. C'est ici que nous disons à GIN d'utiliser le middleware que nous avons créé. Dans Line 22, nous utilisons le middleware Auth qui sera attaché à cet ensemble particulier de points de terminaison. Cool, ouais? Permet d'économiser beaucoup de lignes de code.

Enfin, à la ligne 14, nous exécutons le serveur API sur le port 8080.

Test de l'API Golang avec le client REST VSCode

Comme mentionné précédemment, le principal avantage de l'utilisation de VS Code pour le développement rapide d'API est la possibilité de tester les points de terminaison de l'API directement depuis l'IDE. Ainsi, sous la racine du projet, j'ai créé un nouveau dossier nommé rest , où j'ai placé tous les fichiers .rest contenant des exemples de requêtes API. Nous allons l'utiliser pour tester notre implémentation d'authentification JWT dans Golang.

Tout d'abord, assurez-vous que vous avez installé l'extension REST Client sur votre VS Code.

image 58 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Assurez-vous que votre serveur de base de données est opérationnel. Démarrez votre serveur d'API Golang en exécutant la commande suivante dans le répertoire racine du projet.

go run .

Vous verriez quelque chose comme ça sur votre terminal.

image 55 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Enregistrez un nouvel utilisateur

Créez un nouveau dossier nommé rest et ajoutez un nouveau fichier nommé user.rest

@host = localhost:8080

// Register User
POST http://{{host}}/api/user/register HTTP/1.1
content-type: application/json

{
"name": "Mukesh Murugan",
"username": "mukesh.murugan",
"email": "mukesh@go.com",
"password": "123465789"
}

###

Donc, ce que nous faisons, c'est envoyer une requête POST au point de terminaison api/user/register avec un corps JSON qui définit le nom d'utilisateur, l'e-mail, le nom et le mot de passe de l'utilisateur dont nous avons besoin pour être enregistré dans l'application. Si votre client REST est correctement installé sur votre code VS, vous verrez une option d'envoi de demande au-dessus de la ligne 4. Cliquez dessus !

image 56 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Vous pouvez voir que l'API répond avec l'ID utilisateur, le nom d'utilisateur et l'e-mail avec un code d'état 201. Cela signifie que notre nouvel utilisateur a été enregistré avec succès dans la base de données.

De plus, vous voyez un joli journal sur le terminal de GIN indiquant la demande que nous venons d'envoyer.

image 59 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Connectons-nous à notre MySQL Workbench pour vérifier si l'utilisateur est ajouté à la base de données.

image 57 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Voilà, tout va bien.

Générer un jeton Web JSON

Maintenant que nous avons enregistré l'utilisateur, utilisons ses informations d'identification pour générer de nouveaux JWT.

Créez un nouveau fichier dans le même dossier rest et nommez-le token.rest

@host = localhost:8080

// Generate JWT
POST http://{{host}}/api/token HTTP/1.1
content-type: application/json

{
"email": "mukesh@go.com",
"password": "123465789"
}

###

Ici, nous allons simplement transmettre les informations d'identification de l'utilisateur au point de terminaison api/token. C'est ce qu'un client fera pour générer des jetons d'authentification.

Envoyez la demande. Vous pouvez voir que l'API répond avec un jeton JWT réel.

image 60 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Faisons encore une chose pour le plaisir. Copiez ce jeton et rendez-vous sur jwt.io

Collez le jeton dans la zone de texte Encodé.

image 61 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Ici, vous pouvez voir que la charge utile contient le nom d'utilisateur et l'e-mail de notre utilisateur. Super cool, ouais?

Gardez ce jeton de côté. Nous en aurons besoin lors de notre prochain test, où nous enverrons une requête au point de terminaison api/secured/ping qui se trouve être sécurisé. Le middleware que nous avons créé n'autorisera l'accès que si la demande a un JWT valide dans l'en-tête d'autorisation.

Point de terminaison sécurisé – Middleware Magic

Créez un nouveau fichier nommé secure.rest

@host = localhost:8080

// Access a Secured API Endpoint
GET http://{{host}}/api/secured/ping HTTP/1.1
content-type: application/json
authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im11a2VzaC5tdXJ1Z2FuIiwiZW1haWwiOiJtdWtlc2hAZ28uY29tIiwiZXhwIjoxNjUwNzQzMjA1fQ.7cAcWxvpqJ1DDZ-ZOM2kIKKedeCEWuUzl0Hj2VuMxYA

###

Ici, à la ligne 6, vous pouvez voir que nous avons mentionné l'en-tête d'autorisation et collé un JWT factice. Nous savons donc que ce JWT n'est pas valide. Testons-le.

image 62 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Là, l'application indique que ce JWT particulier a déjà expiré, il y a environ 20 heures. Maintenant, dans notre requête, remplaçons le JWT par celui que nous avons généré précédemment. N'oubliez pas que le JWT n'est valide que pendant 1 heure à partir du moment de la génération. Une fois que vous avez ajouté le nouveau JWT à l'en-tête de la demande, envoyez la demande.

image 63 Implémentation de l'authentification JWT dans l'API Golang REST - Guide détaillé

Super, nous obtenons la réponse ping du point de terminaison sécurisé.

C'est la fin de cet article détaillé sur l'authentification JWT dans l'API Golang REST !

Sommaire

Nous avons appris en détail comment implémenter facilement l'authentification JWT dans les API Golang Rest. En cours de route, nous avons couvert divers sujets tels que les bases de JWT, les premiers pas avec GIN Framework, la configuration de GORM et les migrations MySQL, l'enregistrement des utilisateurs, la génération de jetons à l'aide du package JWT-GO, l'utilisation des middlewares GIN, le hachage et le décryptage des mots de passe à l'aide du package bcrypt, travailler avec Gin Routes et ainsi de suite.

Partagez cet article avec vos collègues et cercles de développement si vous avez trouvé cela intéressant. Vous pouvez trouver le  code source de cette implémentation mentionnée ici . Acclamations! 

 Source : https://codewithmukesh.com/blog/jwt-authentication-in-golang/

#golang #restapi #jwt