1.项目初始化
环境搭建
安装Nginx: 从Nginix官方网站(nginx.org/)下载相应的安装包进行安装
创建项目目录结构
创建一个项目目录,my_sevice,在该目录下创建以下子目录:
main.go:主程序入口文件controllers:重复处理各种API请求的控制器函数的目录models:用于定义数据模型结构体,这些结构体通常与数据库交互或者表示业务逻辑中的数据对象middlewares:放置中间件函数的目录,比如用户认证中间件等utils:包含一些工具函数的目录,例如生成和验证用户认证令牌的函数等
2.构建API接口
2.1 选择Web框架——Gin
Go 语言中有多种 Web 框架可供选择,这里选用 Gin 框架,它具有高性能、简洁易用的特点。
通过以下命令安装 Gin 框架:
go get -u github.com/gin-gonic/gin
2.2 定义API端口与业务逻辑
- 在
main.go文件中,首先导入必要的包并创建一个Gin引擎实例:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// 后续在这里定义具体的API端点和路由逻辑
router.Run(":8080")
}
- 在
models目录下创建一个名为User.go的文件,在里面定义一个User的结构体,用于映射数据库中的用户表结构,代码如下:
package models
import "gorm.io/gorm"
type User struct {
gorm.Model
Name string `json:"name"`
Age int `json:"age"`
}
- 在
controllers目录下创建一个名为hello_controller.go的文件,用于处理与返回相关用户信息的API请求,代码如下:
package controllers
import (
"errors"
"net/http"
"gihub.com/ctc/my_server/models"
"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func GetUserInfo(c *gin.Context) {
//获取用户ID
userID := c.Param("id")
// 连接数据库,根据实际情况修改连接字符串中的用户名、密码、主机和数据库名
dsn := "root:root@tcp(127.0.0.1:3306)/gormstudy?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": "数据库连接失败"})
return
}
//查询用户信息
var user models.User
result := db.First(&user, userID)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
c.JSON(http.StatusNotFound, gin.H{"message": "用户不存在"})
return
} else {
c.JSON(http.StatusInternalServerError, gin.H{"message": "查询用户信息失败"})
}
return
}
//将查询到的用户信息返回给客户端
c.JSON(http.StatusOK, gin.H{
"id": user.ID,
"name": user.Name,
"age": user.Age,
"updatedAt": user.UpdatedAt,
"message": "查询成功,hello good",
})
}
- 在
main.go文件中注册该API端点:
package main
import (
"gihub.com/ctc/my_server/controllers"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
//后续在这里定义具体的API端点和路由逻辑
router.GET("api/users/:id",controllers.GetUserInfo)
router.Run(":8080")
}
3.用户认证
3.1 安装JWT相关库
JWT(JSON Web Token): 是一种流行的用户认证方式,它具有无状态、便于分布式系统应用等优点
执行以下命令安装github.com/dgrijalva/jwt-go库,用于处理 JWT 令牌的生成和验证:
go get -u github.com/dgrijalva/jwt-go
3.2 生成和验证JWT令牌
- 在
utils目录下创建一个名为jwt_utils.go的文件,用于生成和验证JWT令牌,代码如下:
package utils
import (
"time"
"github.com/dgrijalva/jwt-go"
)
const SecretKey = "ctc_shangan"
// GenerateJWT 生成JWT令牌
func GennerateJWT(userID string) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"issuedAt": time.Now(),
"expiresAt": time.Now().Add(time.Hour * 24),
"subject": userID,
})
return token.SignedString([]byte(SecretKey))
}
// VerifyJWT 验证JWT令牌
func VerifyJWT(tokenString string) (*jwt.Token, error) {
return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(SecretKey), nil
})
}
- 创建用户认证中间件。 在
middlewares目录下创建一个名为auth_middleware.go的文件,用于验证JWT令牌并进行相关的认证处理,代码如下:
package middlewares
import (
"net/http"
"gihub.com/ctc/my_server/utils"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
)
// AuthMiddleware 验证JWT令牌的中间件
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
//获取JWT令牌
tokenString := c.GetHeader("Authorization")
if tokenString == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "未提供授权令牌"})
return
}
//除去 "Bearer " 前缀
if len(tokenString) > 7 && tokenString[:7] == "Bearer " {
tokenString = tokenString[7:]
}
//验证JWT令牌
token, err := utils.VerifyJWT(tokenString)
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "无效的授权令牌"})
return
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
//将用户ID添加到上下文中,以便后续使用
c.Set("user_id", claims["subject"])
c.Next()
} else {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "无效的授权令牌"})
return
}
}
}
- 在
main.go文件中应用用户认证中间件
package main
import (
"gihub.com/ctc/my_server/controllers"
"gihub.com/ctc/my_server/middlewares"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
//后续在这里定义具体的API端点和路由逻辑
// 应用用户认证中间件到需要认证的路由
authenticatedRoutes:=router.Group("/api")
authenticatedRoutes.Use(middlewares.AuthMiddleware())
authenticatedRoutes.GET("api/users/:id", controllers.GetUserInfo)
router.Run(":8080")
}
4.配置Nginx
4.1 配置Nginx反向代理
- 打开
nginx.conf文件。 - 在
http节中添加以下反向代理配置内容(Go 服务监听在本地的127.0.0.1:8080端口):
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
listen 80:指定 Nginx 监听的端口为 80,这是常见的 HTTP 服务端口,外部用户通过这个端口访问 Nginx。server_name localhost:设置服务器名称为localhost,你也可以替换成你实际拥有的域名(如果有的话)。当外部用户在浏览器中输入这个域名或者localhost(在本地测试时)时,请求会被发送到这个配置对应的服务器块处理。location /:定义了一个匹配所有请求路径的规则。也就是说,任何以/开头的请求路径都会进入到这个配置块中进行处理。proxy_pass http://127.0.0.1:8080:将匹配到的请求转发到本地的127.0.0.1:8080端口,也就是你的 Go 服务所在的端口。这样外部请求先到达 Nginx,然后 Nginx 会把请求转发给你的 Go 服务进行处理。proxy_set_header系列设置:
proxy_set_header Host $host:设置转发请求时的Host头信息,保持和原始请求的Host一致,这样你的 Go 服务能正确识别请求的来源域名等信息。proxy_set_header X-Real-IP $remote_addr:将客户端的真实 IP 地址传递给 Go 服务,以便在 Go 服务中能准确获取到请求的来源 IP。proxy_set_header X-Forwarded-For $remote_addr:和上面类似,也是用于传递客户端的 IP 信息,在一些复杂的网络环境中,这个头信息可能会更有用,比如在存在多层代理的情况下,它可以记录完整的请求转发路径中的 IP 信息。
- 保存修改后的
nginx.conf文件
5.测试与部署
5.1 本地测试
-
在
main.go文件添加以下内容,用于获取token,进行下面的测试userID := "1" token, err := utils.GennerateJWT(userID) if err != nil { fmt.Println("生成令牌失败:", err) return } fmt.Println("生成的令牌:", token) -
在本地运行你的 Go 服务,在命令行中执行
go run main.go。 -
使用工具
Postman,按照下面的步骤进行测试-
复制打印出来的
token -
点击Postman中的
Headers -
Key:Authorization , value:
token -
点击Send发送
-
6.个人总结
我的VScode安装了MarsCode AI,它会在我打代码的时候实时给我提示,所以哪怕我对Go不是那么熟悉,但是打代码的过程依然比较顺畅。
通过本次项目实践,我在多个方面取得了显著的进步。不仅熟练掌握了 Go 语言中构建 API 接口、实现用户认证以及与数据库交互的相关技术,还深入了解了 Nginx 的反向代理配置原理和使用方法。同时,在项目开发过程中,我更加深刻地体会到了代码结构规划、错误处理以及安全保障等方面的重要性。