· 3 months ago · Jul 10, 2025, 12:55 PM
1package main
2
3import (
4 "net/http"
5 "time"
6
7 "github.com/gin-gonic/gin"
8 "github.com/golang-jwt/jwt/v5"
9 "golang.org/x/crypto/bcrypt"
10)
11
12var (
13 jwtSecret = []byte("your_strong_secret_key")
14 users = make(map[string]string)
15)
16
17type Credentials struct {
18 Username string `json:"username" binding:"required"`
19 Password string `json:"password" binding:"required"`
20}
21
22type Claims struct {
23 Username string `json:"username"`
24 jwt.RegisteredClaims
25}
26
27func main() {
28 r := gin.Default()
29
30 r.POST("/register", registerHandler)
31 r.POST("/login", loginHandler)
32 r.GET("/protected", authMiddleware, protectedHandler)
33
34 r.Run(":8080")
35}
36
37func registerHandler(c *gin.Context) {
38 var creds Credentials
39 if err := c.ShouldBindJSON(&creds); err != nil {
40 c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
41 return
42 }
43
44 if _, exists := users[creds.Username]; exists {
45 c.JSON(http.StatusConflict, gin.H{"error": "User already exists"})
46 return
47 }
48
49 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(creds.Password), bcrypt.DefaultCost)
50 if err != nil {
51 c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not hash password"})
52 return
53 }
54
55 users[creds.Username] = string(hashedPassword)
56 c.JSON(http.StatusCreated, gin.H{"message": "User created"})
57}
58
59func loginHandler(c *gin.Context) {
60 var creds Credentials
61 if err := c.ShouldBindJSON(&creds); err != nil {
62 c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
63 return
64 }
65
66 storedHash, exists := users[creds.Username]
67 if !exists {
68 c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
69 return
70 }
71
72 if err := bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(creds.Password)); err != nil {
73 c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
74 return
75 }
76
77 expirationTime := time.Now().Add(15 * time.Minute)
78 claims := &Claims{
79 Username: creds.Username,
80 RegisteredClaims: jwt.RegisteredClaims{
81 ExpiresAt: jwt.NewNumericDate(expirationTime),
82 },
83 }
84
85 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
86 tokenString, err := token.SignedString(jwtSecret)
87 if err != nil {
88 c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
89 return
90 }
91
92 c.JSON(http.StatusOK, gin.H{"token": tokenString})
93}
94
95func authMiddleware(c *gin.Context) {
96 tokenString := c.GetHeader("Authorization")
97 if tokenString == "" {
98 c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing authorization header"})
99 return
100 }
101
102 token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
103 return jwtSecret, nil
104 })
105
106 if err != nil || !token.Valid {
107 c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
108 return
109 }
110
111 claims, ok := token.Claims.(*Claims)
112 if !ok {
113 c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
114 return
115 }
116
117 c.Set("username", claims.Username)
118 c.Next()
119}
120
121func protectedHandler(c *gin.Context) {
122 username, exists := c.Get("username")
123 if !exists {
124 c.JSON(http.StatusInternalServerError, gin.H{"error": "User not found in context"})
125 return
126 }
127
128 c.JSON(http.StatusOK, gin.H{"message": "Hello " + username.(string) + "! This is protected content"})
129}