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

98 阅读7分钟

本文章分为上下两章,在上篇文章里我们将介绍API的概念、快速构建服务、使用 gin 构建 API 接口、JWT。在下篇文章里我们将使用本文章所讲述的技术实现用户认证功能API。

1 API概念

API(Application Programming Interface,应用程序编程接口)是一种允许不同软件应用程序之间进行交互的方式。它定义了一组规则和协议,通过这些规则和协议,不同的软件组件、服务或应用可以相互通信和交换数据。API 允许开发人员在不暴露内部实现细节的情况下,与外部组件进行集成。

API 可以用于不同类型的应用,包括:

  1. Web API
    Web API 是通过网络访问的一种 API,通常使用 HTTP 协议。Web API 可以提供数据、服务、功能等,供其他应用或客户端使用。常见的例子包括 RESTful API、GraphQL 等。
  2. 库 API
    库 API 是针对特定编程语言的一组函数、方法和类,开发人员可以通过调用这些函数来访问库中提供的功能。例如,标准库或第三方库提供的函数集合。
  3. 操作系统 API
    操作系统 API 允许应用程序与操作系统进行交互,访问底层系统资源和功能,如文件系统、网络、内存管理等。
  4. 硬件 API
    硬件 API 允许应用程序与硬件设备进行交互,如图形处理器、传感器、打印机等。

API 的重要性在于它们允许不同的软件系统协同工作,实现功能复用、解耦合、模块化开发等优势。通过定义清晰的接口,API 可以促进不同开发团队、不同项目之间的合作,同时也可以为开发人员提供更高效的开发体验。常见的 API 通信方式包括使用 HTTP 请求和响应,如 GET、POST、PUT、DELETE 等。

2 快速构建

以下短短几行代码即可快速构建并启动API服务。要访问API只需要在浏览器中输入http://localhost:8080/test即可看到“Hello!”。

http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprintf(w, "Hello!")})
log.Fatal(http.ListenAndServe(":8080", nil))

3 使用 gin 构建 API 接口

3.1 gin的介绍

Gin 是一个基于 Go 语言的轻量级 Web 框架,专注于高性能和开发效率。它是一个快速的 HTTP 框架,旨在帮助开发人员构建高性能的 Web 应用程序和 API 服务。

以下是 Gin 框架的一些特点和功能:

  1. 快速:Gin 通过最小化框架内部的功能和开销,追求极致的性能。它采用了一些优化措施,如使用 Radix 树进行路由匹配,从而使得处理请求的速度更快。
  2. 轻量级:Gin 的代码库相对较小,它只包含一些基本的功能,这使得它很容易上手并且不会引入过多的复杂性。
  3. 路由:Gin 提供了灵活的路由设置和参数解析,支持 RESTful 风格的路由设计,使得创建 API 接口变得简单和直观。
  4. 中间件:Gin 支持中间件,开发人员可以在请求处理过程中插入额外的逻辑,例如日志记录、认证、跨域处理等。
  5. 错误处理:Gin 提供了一种统一的错误处理机制,开发人员可以返回具有适当状态码和错误消息的错误响应。
  6. JSON 解析和序列化:Gin 提供了方便的 JSON 解析和序列化功能,可以轻松地处理请求和响应中的 JSON 数据。
  7. HTML 模板渲染:虽然 Gin 主要用于构建 API 接口,但它也支持基本的 HTML 模板渲染功能,使其适用于一些 Web 应用场景。
  8. 扩展性:虽然 Gin 是一个轻量级框架,但它支持自定义中间件、插件和拓展,开发人员能够根据需求扩展功能。

3.2 使用gin快速构建

同样的,我们要访问API只需要在浏览器中输入http://localhost:8080/test即可看到“Hello!”。

	engine := gin.Default()
	engine.GET("/test", func(c *gin.Context) {
		c.String(http.StatusOK, "Hello!")
	})
	engine.Run(":8080")

以上主要分为以下三步:

  • 初始化http服务对象(gin.Default)
  • 设置路由(engine.GET)
  • 监听服务(engine.Run)

3.3 HTTP的方法

在 RESTful API 中,使用的主要是以下五种HTTP方法:

  1. GET,表示读取服务器上的资源
  2. POST,表示在服务器上创建资源
  3. PUT,表示更新或者替换服务器上的资源
  4. DELETE,表示删除服务器上的资源
  5. PATCH,表示更新/修改资源的一部分

此外还有不常用的HEAD、OPTIONS、TRACE、CONNECT等方法

要实现以上方法,只需要engine.method(*)就可以了。如果要实现所有method,只需要engine.Any(*)就可以了。在这里给大家看一下Any的实现函数:

var	anyMethods = []string{
		http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch,
		http.MethodHead, http.MethodOptions, http.MethodDelete, http.MethodConnect,
		http.MethodTrace,
	}

// Any registers a route that matches all the HTTP methods.
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
	for _, method := range anyMethods {
		group.handle(method, relativePath, handlers)
	}
	return group.returnObj()
}

3.4 拆分

在真正写服务中我们不可能会把一大堆代码写到一个.go文件中,而是会拆分为controller包、repository包、util和public等包,其中public放置静态资源文化,而controller包中就是进行处理的Handler,再有就是repository包用来连接数据库。对于路由的注册习惯是写一个router.go文件来统一管理路由。

4 JWT

4.1 JWT的概念

JWT(JSON Web Token)是一种用于在网络应用间传递信息的开放标准(RFC 7519)。它以 JSON 格式存储信息,通过数字签名或加密来确保信息的安全传输。JWT 通常用于身份验证和授权,允许两个系统之间安全地传递信息,以便进行用户身份验证和授权控制。

JWT 由三部分组成,每个部分之间使用点号(.)分隔:

  1. Header(头部) :包含两部分信息:令牌类型(通常是 "JWT")和所使用的签名算法,如 HMAC SHA256 或 RSA。
  2. Payload(载荷) :也称为声明,包含需要传递的信息。声明分为注册声明(Registered Claims)、公共声明(Public Claims)和私有声明(Private Claims)。注册声明包括常见的声明,如发行人、过期时间等。公共声明包含不敏感的信息,可以自定义添加。私有声明包含应用程序特定的信息。
  3. Signature(签名) :使用头部和载荷以及一组密钥进行签名,以确保消息的完整性和认证。签名部分用于验证发送者是否为其声称的发送者,以及载荷在传输过程中是否被篡改。

JWT 的工作流程通常如下:

  1. 用户登录时,服务器生成一个 JWT 并将用户信息和其他所需信息添加到载荷部分。
  2. 服务器使用密钥对 JWT 进行签名,生成签名部分。
  3. 服务器将生成的 JWT 返回给客户端。
  4. 客户端在以后的请求中将 JWT 添加到请求的头部或其他位置,以便服务器验证用户身份。
  5. 服务器接收到 JWT 后,可以验证签名、解析载荷并执行授权和认证操作。

JWT 具有以下优点:

  • 轻量且自包含:JWT 可以在令牌中携带用户信息,避免了频繁的数据库查询,因此适用于分布式系统。
  • 无状态:JWT 本身包含了所有必要的信息,服务器不需要在后端存储会话信息。
  • 跨域和多平台支持:JWT 可以在不同的系统和域之间使用,方便传递认证信息。

需要注意:

  • JWT 中的信息是可解码的,因此不应在 JWT 中存储敏感信息。
  • 由于 JWT 是无状态的,一旦签发,就不能撤销。如果需要撤销访问权限,需要等待 JWT 过期。

4.2 JWT的使用

使用JWT主要有以下几步:

  • 获取request
  • 根据传入的request生成token
  • 返回response

具体JWT的使用见下篇文章