如何将我的服务开放给用户:构建 API 接口和用户认证的实践指南|豆包MarsCode AI刷题

60 阅读5分钟

1.项目初始化

环境搭建

安装Nginx: 从Nginix官方网站(nginx.org/)下载相应的安装包进行安装

创建项目目录结构

创建一个项目目录,my_sevice,在该目录下创建以下子目录:

  • main.go :主程序入口文件
  • controllers:重复处理各种API请求的控制器函数的目录
  • models:用于定义数据模型结构体,这些结构体通常与数据库交互或者表示业务逻辑中的数据对象
  • middlewares:放置中间件函数的目录,比如用户认证中间件等
  • utils:包含一些工具函数的目录,例如生成和验证用户认证令牌的函数等

image.png

2.构建API接口

2.1 选择Web框架——Gin

Go 语言中有多种 Web 框架可供选择,这里选用 Gin 框架,它具有高性能、简洁易用的特点。

通过以下命令安装 Gin 框架:

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

image.png

2.2 定义API端口与业务逻辑

  1. main.go文件中,首先导入必要的包并创建一个Gin引擎实例:
package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    // 后续在这里定义具体的API端点和路由逻辑

    router.Run(":8080")
}

  1. models目录下创建一个名为User.go的文件,在里面定义一个User的结构体,用于映射数据库中的用户表结构,代码如下:
package models

import "gorm.io/gorm"

type User struct {
	gorm.Model
	Name string `json:"name"`
	Age  int    `json:"age"`
}
  1. 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",
	})

}
  1. 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

image.png

3.2 生成和验证JWT令牌

  1. 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
	})
}
  1. 创建用户认证中间件。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
		}
	}
}
  1. 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反向代理

  1. 打开 nginx.conf 文件。
  2. 在 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;
    }
}

image.png

  • 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 信息。
  1. 保存修改后的nginx.conf文件

5.测试与部署

5.1 本地测试

  1. main.go文件添加以下内容,用于获取token,进行下面的测试

    userID := "1"
            token, err := utils.GennerateJWT(userID)
            if err != nil {
                    fmt.Println("生成令牌失败:", err)
                    return
            }
            fmt.Println("生成的令牌:", token)
    
    
  2. 在本地运行你的 Go 服务,在命令行中执行go run main.goimage.png

  3. 使用工具Postman,按照下面的步骤进行测试

    • 复制打印出来的token image.png

    • 点击Postman中的Headers

    • Key:Authorization , value:token image.png

    • 点击Send发送 image.png

6.个人总结

我的VScode安装了MarsCode AI,它会在我打代码的时候实时给我提示,所以哪怕我对Go不是那么熟悉,但是打代码的过程依然比较顺畅。

通过本次项目实践,我在多个方面取得了显著的进步。不仅熟练掌握了 Go 语言中构建 API 接口、实现用户认证以及与数据库交互的相关技术,还深入了解了 Nginx 的反向代理配置原理和使用方法。同时,在项目开发过程中,我更加深刻地体会到了代码结构规划、错误处理以及安全保障等方面的重要性。