Building a Web Server with Golang, Gin, MySQL, Redis, JWT, and Session Authentication
In this tutorial, we will create a web server using Golang, the Gin framework, MySQL for the database, Redis for session management, and JWT for secure authentication.
Project Setup
Step 1: Create Project Directory and Initialize
First, create a project directory and initialize it.
mkdir
myappcd
myapp
go mod init myapp
Step 2: Install Dependencies
Install the necessary packages.
go get github.com/gin-gonic/gin
go get github.com/jinzhu/gorm
go get github.com/jinzhu/gorm/dialects/mysql
go get github.com/go-redis/redis/v8
go get github.com/dgrijalva/jwt-go
go get github.com/gin-contrib/sessions
go get github.com/gin-contrib/sessions/redis
Code Implementation
Step 3: Project Structure
Set up the project structure as follows:
myapp/├── main.go
├── models/│ └── user.go
├── handlers/│ └── auth.go
└── middleware/ └── auth.go
Step 4: Define the User Model
Create the models/user.go
file and define the User model.
package
modelsimport "github.com/jinzhu/gorm"
{
type User struct
gorm.Model Username string `json:"username" gorm:"unique"`
Password string `json:"password"`
}
Step 5: Set Up Database Connection
In main.go
, set up connections to MySQL and Redis, and initialize the Gin server.
package
mainimport
( "github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/go-redis/redis/v8"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/redis"
"log"
"myapp/models"
"myapp/handlers"
"context"
)var
db *gorm.DBvar
rdb *redis.Clientvar err error
{
func main() db, err = gorm.Open("mysql", "root:password@tcp(127.0.0.1:3306)/mydb?charset=utf8&parseTime=True&loc=Local"
) if err != nil
{
log.Fatal(err)
} defer
db.Close()
db.AutoMigrate(&models.User{})
rdb = redis.NewClient(&redis.Options{ Addr: "localhost:6379"
,
})
_, err = rdb.Ping(context.Background()).Result() if err != nil
{
log.Fatal(err)
}
r := gin.Default() store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"
)) r.Use(sessions.Sessions("mysession"
, store)) r.POST("/login"
, handlers.Login) r.POST("/signup"
, handlers.Signup) r.GET("/profile"
, handlers.Profile) r.Run(":8080"
)
}
Step 6: Create Handlers
Define the authentication handlers in handlers/auth.go
.
package
handlersimport
( "github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/dgrijalva/jwt-go"
"github.com/gin-contrib/sessions"
"golang.org/x/crypto/bcrypt"
"time"
"net/http"
"myapp/models"
)var jwtKey = []byte("my_secret_key"
)type Claims struct
{ Username string `json:"username"`
jwt.StandardClaims
}func Signup(c *gin.Context)
{ var
user models.User if err := c.ShouldBindJSON(&user); err != nil
{ c.JSON(http.StatusBadRequest, gin.H{"error"
: err.Error()}) return
} hashedPassword, err := bcrypt.GenerateFromPassword([]byte
(user.Password), bcrypt.DefaultCost) if err != nil
{ c.JSON(http.StatusInternalServerError, gin.H{"error"
: err.Error()}) return
} user.Password = string
(hashedPassword) if err := models.DB.Create(&user).Error; err != nil
{ c.JSON(http.StatusInternalServerError, gin.H{"error"
: err.Error()}) return
} c.JSON(http.StatusOK, gin.H{"message": "User registered successfully"
})
}func Login(c *gin.Context)
{ var
user models.User if err := c.ShouldBindJSON(&user); err != nil
{ c.JSON(http.StatusBadRequest, gin.H{"error"
: err.Error()}) return
} var
dbUser models.User if err := models.DB.Where("username = ?", user.Username).First(&dbUser).Error; err != nil
{ c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"
}) return
} if err := bcrypt.CompareHashAndPassword([]byte(dbUser.Password), []byte(user.Password)); err != nil
{ c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"
}) return
} expirationTime := time.Now().Add(5
* time.Minute)
claims := &Claims{
Username: user.Username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtKey) if err != nil
{ c.JSON(http.StatusInternalServerError, gin.H{"error"
: err.Error()}) return
}
session := sessions.Default(c) session.Set("token"
, tokenString)
session.Save() c.JSON(http.StatusOK, gin.H{"token"
: tokenString})
}func Profile(c *gin.Context)
{
session := sessions.Default(c) tokenString := session.Get("token"
) if tokenString == nil
{ c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"
}) return
}
claims := &Claims{} token, err := jwt.ParseWithClaims(tokenString.(string), claims, func(token *jwt.Token) (interface{}, error
) { return jwtKey, nil
}) if err != nil
|| !token.Valid { c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"
}) return
} c.JSON(http.StatusOK, gin.H{"username"
: claims.Username})
}
Step 7: Create Middleware
Define the authentication middleware in middleware/auth.go
.
package
middlewareimport
( "github.com/gin-gonic/gin"
"github.com/gin-contrib/sessions"
"net/http"
"github.com/dgrijalva/jwt-go"
"myapp/handlers"
)func AuthMiddleware()
gin.HandlerFunc { return func(c *gin.Context)
{
session := sessions.Default(c) tokenString := session.Get("token"
) if tokenString == nil
{ c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"
})
c.Abort() return
}
claims := &handlers.Claims{} token, err := jwt.ParseWithClaims(tokenString.(string), claims, func(token *jwt.Token) (interface{}, error
) { return handlers.jwtKey, nil
}) if err != nil
|| !token.Valid { c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"
})
c.Abort() return
} c.Set("username"
, claims.Username)
c.Next()
}
}
Step 8: Run and Test the Server
Run the server and test the endpoints.
go run main.go
You can use Postman or curl to test the API endpoints.
- Signup:
curl -X POST http://localhost:8080/signup -H "Content-Type: application/json" -d '{"username": "user1", "password": "password"}'
- Login:
curl -X POST http://localhost:8080/login -H "Content-Type: application/json" -d '{"username": "user1", "password": "password"}'
- Profile:
curl -X GET http://localhost:8080/profile --cookie "mysession=<your_session_id>"
By following these steps, you can build a web server with Golang, Gin, MySQL, Redis, JWT, and session authentication. If you have any questions, feel free to ask!