构建 API 接口
RESTful API
RESTful API(Representational State Transferful Application Programming Interface)是一种设计和构建网络应用程序的架构风格,用于处理资源的访问和操作。它遵循一组约定和规则,使得客户端和服务器之间的通信变得简单、灵活和可伸缩。 以下是RESTful API的主要特点和概念:
-
资源(Resources): 在RESTful API中,所有数据都被视为资源。每个资源都有一个唯一的标识符(通常是URL),通过这个标识符来访问和操作资源。
-
HTTP方法(HTTP Methods): RESTful API使用HTTP方法来表示对资源的不同操作。常用的HTTP方法包括:
- GET:获取资源的信息。
- POST:创建新资源。
- PUT:更新现有资源。
- DELETE:删除资源。
-
状态无关性(Stateless): RESTful API的通信是无状态的,每个请求都是独立的。服务器不会存储客户端的状态,所有必要的信息都包含在请求中。
-
统一接口(Uniform Interface): RESTful API采用一组统一的接口约定,包括使用统一的资源标识符(URL)来访问资源,使用标准的HTTP方法来操作资源,以及通过HTTP状态码来传达请求的结果。
-
资源的表示(Representation): 客户端可以通过不同的表示形式(如JSON、XML等)获取资源的信息。服务器将资源的状态以适当的表示形式返回给客户端。
-
超媒体驱动(HATEOAS): 这是RESTful API的一个特点,它允许服务器在响应中提供相关资源的链接,从而允许客户端动态地探索和访问其他相关资源。
-
缓存支持: RESTful API支持HTTP的缓存机制,以提高性能和降低服务器负载。
RESTful API的设计和使用遵循一些最佳实践,例如:
- 使用有意义的URL,用于表示资源的层次结构和关系。
- 使用适当的HTTP状态码来表示请求的结果,如成功、错误等。
- 使用合适的HTTP头部来传递元数据,如身份验证信息、内容类型等。
通过遵循这些原则,RESTful API可以提供灵活、可扩展和易于理解的接口,使客户端和服务器之间的通信变得更加简单和高效。
Gin 框架
项目需要引入 Gin 框架。可以使用以下命令安装 Gin:
go get -u github.com/gin-gonic/gin
构建基础API接口
首先,创建一个简单的 API,使用 GET 获取资源的信息,访问 http://localhost:8080/hello 即可获取到hello world
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello world",
})
})
r.Run(":8080")
}
同时 Gin 还提供了中间件的支持,您可以在请求处理前后执行某些操作,如日志记录、身份验证等。
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 执行前置操作
log.Println("Request received")
// 继续处理请求
c.Next()
// 执行后置操作
log.Println("Request handled")
}
}
func main() {
r := gin.Default()
r.Use(LoggerMiddleware())
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello world",
})
})
r.Run(":8080")
}
用户认证
什么是用户认证
用户认证是确认用户身份的过程,确保只有合法用户可以访问你的服务。在API开放给公众使用时,用户认证尤为重要,防止未经授权的访问和滥用。
常见的用户认证方式
- 基本认证:用户通过在请求头中提供用户名和密码进行认证。但是这种方式不够安全,因为密码是明文传输的,容易被截获。
- Bearer令牌认证:用户在请求头中使用令牌来认证。令牌通常是由服务器签发的加密字符串,比较安全,不需要传输密码。
- OAuth认证:OAuth是一个用于授权的开放标准,允许用户授权第三方应用访问他们的资源。这是常见的社交媒体登录方式。
JWT
JWT(JSON Web Token) 是一种用于安全地在不同实体之间传递信息的开放标准。它通常用于身份验证和授权,以及在应用程序之间安全地传输声明(claims)。JWT是一种紧凑且自包含的格式,以JSON格式表示,可以通过网络传输,并且在受信任的实体之间进行验证。 一个 JWT 通常由三个部分组成,通过点号 . 分隔:
- Header(头部) :包含了令牌的类型(通常是 "JWT")和使用的加密算法,例如 HMAC SHA256 或 RSA。
- Payload(负载) :包含了一些声明(claims),如令牌的主题(subject)、到期时间(expiration time)、发布者(issuer)等。这些信息是关于令牌的附加信息。
- Signature(签名) :使用头部和负载以及一个密钥(secret)来创建,以确保令牌没有被篡改。
安装依赖
为了实现用户认证,使用 jwt-go 包来处理JWT令牌
go get -u github.com/dgrijalva/jwt-go
登录路由
创建登录的路由,用于用户的登录操作和获取token
type UserInfo struct {
Username string `json:"username"`
Password string `json:"password"`
}
func authHandler(c *gin.Context) {
var user UserInfo
err := c.ShouldBind(&user)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2001,
"msg": "无效的参数",
})
return
}
if user.Username == "cc" && user.Password == "123456" {
//生成token
tokenString, _ := GenToken(user.Username)
c.JSON(http.StatusOK, gin.H{
"code": 200,
"msg": "success",
"data": gin.H{"token": tokenString},
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 2002,
"msg": "鉴权失败",
})
return
}
获取信息路由
用登录路由生成的 token 获取用户信息
func homeHandler(c *gin.Context) {
username := c.MustGet("username").(string)
c.JSON(http.StatusOK, gin.H{
"code": 2000,
"msg": "success",
"data": gin.H{"username": username},
})
}
生成JWT
在登录成功后,生成一个 JWT 并返回给客户端。这个 JWT 包含用户的 ID 信息,并在有效期内可用于后续请求的认证。
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
// 定义过期时间
const TokenExpireDuration = time.Hour * 2
//定义secret
var MySecret = []byte("这是一段用于生成token的密钥")
//生成jwt
func GenToken(username string) (string, error) {
c := MyClaims{
username,
jwt.StandardClaims{
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(),
Issuer: "my-project",
},
}
//使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
//使用指定的secret签名并获得完成的编码后的字符串token
return token.SignedString(MySecret)
}
解析JWT
//解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
//解析token
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
return MySecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
基于JWT认证中间件
创建了一个中间件函数用于验证 JWT,然后使用这个中间件来保护需要认证的 API 路由
//基于JWT认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
c.JSON(http.StatusOK, gin.H{
"code": 2003,
"msg": "请求头中的auth为空",
})
c.Abort()
return
}
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusOK, gin.H{
"code": 2004,
"msg": "请求头中的auth格式错误",
})
//阻止调用后续的函数
c.Abort()
return
}
mc, err := ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2005,
"msg": "无效的token",
})
c.Abort()
return
}
//将当前请求的username信息保存到请求的上下文c上
c.Set("username", mc.Username)
//后续的处理函数可以通过c.Get("username")来获取请求的用户信息
c.Next()
}
}
保护 API 路由
使用JWTAuthMiddleware中间件来保护需要认证的 API 路由,只有在提供有效 JWT 的情况下才能访问 /home 路由。
func main() {
r := gin.Default()
r.POST("/auth", authHandler)
r.GET("/home", JWTAuthMiddleware(), homeHandler)
r.Run(":9000")
}