如何将我的服务开放给用户:构建 API 接口和用户认证的实践 | 青训营

577 阅读6分钟

0. 前情提要

在学习完 Gin 和 Gorm 之后,我们逐步完成了后端部分的代码,而对于前后端分离开发模式下,后端工程师需要通过查询前端提供的接口文档,参与共同定义接口(分为四部分:方法、uri、请求参数、返回参数),并根据这个接口进行后端代码的实现。因此学习如何构建API接口对于项目的开发尤为重要。

1. 构建API接口

1.1. 构建基础API接口

首先,我们必须创建一个非常简单的服务器来处理HTTP请求。

为此,我们创建一个名为 main.go 的新文件。 在这个 main.go 文件中,我们将要定义3个不同的函数。

  • page函数,是一个处理HTTP请求的处理程序。它接受两个参数:w 是一个 http.ResponseWriter,用于向客户端发送响应,r 是一个 http.Request,包含了客户端发送的请求信息。
  • router函数,它设置了路由和启动HTTP服务器。
  • main函数,它则用于启动API
    package main
    ​
    import (
        "fmt"
        "log"
        "net/http"
    )
    ​
    func page(w http.ResponseWriter, r *http.Request){
        fmt.Fprintf(w, "Hello!")
    }
    ​
    func router() {
        http.HandleFunc("/", page)
        log.Fatal(http.ListenAndServe(":8090", nil))
    }
    ​
    func main() {
        router()
    }

在写好后我们点击运行,浏览器打开http://localhost:8090/,就可以看到 Hello! ,说明我们现在已经成功创建了一个基础的API。

1.png

1.2. RESTful API

RESTful API(Representational State Transferful Application Programming Interface)是一种设计和构建网络应用程序的架构风格,用于处理资源的访问和操作。它遵循一组约定和规则,使得客户端和服务器之间的通信变得简单、灵活和可伸缩。

以下是RESTful API的主要特点和概念:

  1. 资源(Resources): 在RESTful API中,所有数据都被视为资源。每个资源都有一个唯一的标识符(通常是URL),通过这个标识符来访问和操作资源。

  2. HTTP方法(HTTP Methods): RESTful API使用HTTP方法来表示对资源的不同操作。常用的HTTP方法包括:

    • GET:获取资源的信息。
    • POST:创建新资源。
    • PUT:更新现有资源。
    • DELETE:删除资源。
  3. 状态无关性(Stateless): RESTful API的通信是无状态的,每个请求都是独立的。服务器不会存储客户端的状态,所有必要的信息都包含在请求中。

  4. 统一接口(Uniform Interface): RESTful API采用一组统一的接口约定,包括使用统一的资源标识符(URL)来访问资源,使用标准的HTTP方法来操作资源,以及通过HTTP状态码来传达请求的结果。

  5. 资源的表示(Representation): 客户端可以通过不同的表示形式(如JSON、XML等)获取资源的信息。服务器将资源的状态以适当的表示形式返回给客户端。

  6. 超媒体驱动(HATEOAS): 这是RESTful API的一个特点,它允许服务器在响应中提供相关资源的链接,从而允许客户端动态地探索和访问其他相关资源。

  7. 缓存支持: RESTful API支持HTTP的缓存机制,以提高性能和降低服务器负载。

RESTful API的设计和使用遵循一些最佳实践,例如:

  • 使用有意义的URL,用于表示资源的层次结构和关系。
  • 使用适当的HTTP状态码来表示请求的结果,如成功、错误等。
  • 使用合适的HTTP头部来传递元数据,如身份验证信息、内容类型等。

通过遵循这些原则,RESTful API可以提供灵活、可扩展和易于理解的接口,使客户端和服务器之间的通信变得更加简单和高效。

1.3. 使用Gin优化接口

Gin 是一个基于 Go 语言的轻量级 Web 框架,专注于高性能的路由和中间件支持。

而第一步便是我们的安装操作,我们可以使用 Go 的包管理工具 go get 进行安装。然后在项目中引入 Gin 包。

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

安装完成后,我们就可以很容易地创建路由和处理程序函数。路由是将 URL 路径映射到处理程序函数的方式。

package main
​
import (
    "github.com/gin-gonic/gin"
)
​
func main() {
    router := gin.Default()
​
    router.GET("/hello", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "Hello, World!",
        })
    })
​
    router.Run(":8090")
}

同时 Gin 还提供了中间件的支持,您可以在请求处理前后执行某些操作,如日志记录、身份验证等。

func LoggerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 执行前置操作
        log.Println("Request received")
​
        // 继续处理请求
        c.Next()
​
        // 执行后置操作
        log.Println("Request handled")
    }
}
​
func main() {
    router := gin.Default()
    router.Use(LoggerMiddleware())
​
    router.GET("/hello", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "Hello, World!",
        })
    })
​
    router.Run(":8090")
}

2. 用户认证

2.1. 什么是用户认证

用户认证是确认用户身份的过程,确保只有合法用户可以访问你的服务。在API开放给公众使用时,用户认证尤为重要,防止未经授权的访问和滥用。

2.2. 常见的用户认证方式

  • 基本认证:用户通过在请求头中提供用户名和密码进行认证。但是这种方式不够安全,因为密码是明文传输的,容易被截获。
  • Bearer令牌认证:用户在请求头中使用令牌来认证。令牌通常是由服务器签发的加密字符串,比较安全,不需要传输密码。
  • OAuth认证:OAuth是一个用于授权的开放标准,允许用户授权第三方应用访问他们的资源。这是常见的社交媒体登录方式。

在Go语言中,我们一般使用 jwt-go 包来处理JWT令牌。

2.3. JWT

JWT(JSON Web Token) 是一种用于安全地在不同实体之间传递信息的开放标准。它通常用于身份验证和授权,以及在应用程序之间安全地传输声明(claims)。JWT是一种紧凑且自包含的格式,以JSON格式表示,可以通过网络传输,并且在受信任的实体之间进行验证。

一个 JWT 通常由三个部分组成,通过点号 . 分隔:

  • Header(头部) :包含了令牌的类型(通常是 "JWT")和使用的加密算法,例如 HMAC SHA256 或 RSA。
  • Payload(负载) :包含了一些声明(claims),如令牌的主题(subject)、到期时间(expiration time)、发布者(issuer)等。这些信息是关于令牌的附加信息。
  • Signature(签名) :使用头部和负载以及一个密钥(secret)来创建,以确保令牌没有被篡改。

下面的代码中,我们实现了一个简单的JWT令牌认证中间件,用户在请求头中提供JWT令牌进行认证。

package main
​
import (
    "fmt"
    "net/http"
    "github.com/gorilla/mux"
    "github.com/dgrijalva/jwt-go"
)
​
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        tokenString := r.Header.Get("Authorization")
        if tokenString == "" {
            http.Error(w, "未提供令牌", http.StatusUnauthorized)
            return
        }
​
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            return []byte("your-secret-key"), nil
        })
​
        if err != nil || !token.Valid {
            http.Error(w, "令牌无效", http.StatusUnauthorized)
            return
        }
​
        next(w, r)
    }
}
​
func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, authenticated Gophers!")
}
​
func main() {
    r := mux.NewRouter()
​
    r.HandleFunc("/hello", authMiddleware(helloHandler))
    http.Handle("/", r)
​
    http.ListenAndServe(":8090", nil)
}

3. 总结

通过构建API接口和用户认证,服务将变得更加强大和灵活,并且得到更多用户的喜爱和信赖。

同时提供清晰明了的API文档,也可以方便用户更好地理解和使用自己的服务。

文章中只是简单介绍了相关知识,可能还有欠缺,还请多多包涵。