如何将我的服务开放给用户:构建 API 接口和用户认证的实践指南 | 青训营
概述
在学习完 Gin 和 Gorm 框架后,我们已经可以完成后端代码的编写。但在前后端分离的开发模式下,后端工程师还需要与前端团队一起协作定义接口。一个完整的接口通常包含四个部分:
- 方法(Method):GET、POST、PUT等
- URI:资源的定位路径
- 请求参数(Request):访问接口时需要传入的数据
- 返回参数(Response):接口返回给调用方的数据
后端工程师需要根据前端提供的接口文档,把接口的输入输出定义清楚,然后实现接口逻辑。
所以,学习如何设计良好的 API 接口对整个项目的开发非常重要。好的接口可以提高项目的健壮性和灵活性。
在构建接口时,后端工程师需要注意:
- 接口路径要合理,比如按资源类型分类
- 输入输出采用统一格式,比如 JSON
- 对参数进行校验,返回适当的错误提示
- 编写详细的接口文档,便于调用方理解
- 要有效地使用 HTTP 方法和状态码
- 合理使用版本控制
与前端团队充分沟通,理解接口需求,制定良好的接口契约,是后端工程师在前后端分离项目中非常重要的一项工作。这需要持续学习和练习接口设计的方法。
定义 API 接口
API(Application Programming Interface)接口,是服务opened对外提供能力和数据的接口。良好的 API 接口需要满足:
- 易于理解,接口需要有良好的文档与示例代码
- 易于使用,接口交互简单、容错率高
- 安全,接口需要身份验证、防篡改、限流等安全措施
下面是一些设计良好 API 接口的准则:
- 使用 HTTPS 保证传输安全性
- 使用 RESTful 设计接口
- 参数与返回结果采用 JSON 格式
- 提供完整的接口文档与示例代码
- 版本化接口,区分开发版和生产版
- 合理使用 HTTP 方法(GET/POST/PUT/DELETE)
- 合理使用 HTTP 状态码
- 接口身份认证(以 API Key 作为 Token)
- 有效使用缓存提高响应速度
具体在gin中,可以在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()
}
实施用户认证
API 开放后,就需要限制未经授权的调用。这里介绍几种常用的 API 用户认证方案:
API Key
- 为每个用户生成一个 API Key,作为标识
- 调用接口时在请求中添加 API Key
- 服务端验证 API Key 的有效性
优点:
- 使用简单
- 无状态,不需要在服务端保存会话状态
缺点:
- API Key 很容易泄露
- 无法有效注销用户
基于 OAuth 2.0 的认证
OAuth 2.0 是行业标准的认证协议,可以不同的 Grant Type 用于不同的场景:
- Authorization Code - 用于 Web 应用认证用户
- Implicit - 用于移动应用认证用户
- Resource Owner Password Credentials - 直接使用用户名密码认证
- Client Credentials - 用于服务器到服务器的认证
优点:
- 更加安全,通过访问令牌来代替 API Key
- 状态化,可以注销用户的访问
缺点:
- 认证流程更复杂
- 需要运行认证服务器
JSON Web Token(JWT)
JWT 可以用于在各服务间传递可验证的用户身份信息。
包括以下几个部分:
Header(头部) :包含了令牌的类型(通常是 "JWT")和使用的加密算法,例如 HMAC SHA256 或 RSA。
Payload(负载) :包含了一些声明(claims),如令牌的主题(subject)、到期时间(expiration time)、发布者(issuer)等。这些信息是关于令牌的附加信息。
Signature(签名) :使用头部和负载以及一个密钥(secret)来创建,以确保令牌没有被篡改。
主要流程是:
- 用户登录后,服务器生成一个 JWT 字符串,发送给客户端
- 客户端保存 JWT,并在调用接口时带上 JWT
- 服务端验证 JWT 的有效性,确认用户身份
优点:
- 更容易实现无状态认证
- JWT 可以包含扩展信息
缺点:
- JWT 更易遭篡改,需要签名验证
- 不容易注销 JWT
综合考虑,我推荐使用 OAuth 2.0 + JWT 的方案,来对 API 调用进行用户认证。这可以使接口既安全又方便使用。
一个使用JWT实现接口认证的简单示例:
// 导入JWT相关包
import (
"github.com/dgrijalva/jwt-go"
"time"
)
// 创建JWT中间件
func JWTAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.Request.Header.Get("Authorization")
// 解析token
claims := &Claims{}
tkn, err := jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
// 校验token
if err != nil || !tkn.Valid {
c.AbortWithStatus(401)
return
}
// token校验通过,将claims信息设置到请求的context中
c.Set("claims", claims)
}
}
// 签发JWT
func GenerateToken(userID uint) (string, error) {
claims := Claims{
UserID: userID,
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
}
}
// 使用HS256算法进行token签名
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtKey)
}
在认证需要的接口中,使用JWTAuthMiddleware中间件即可完成JWT的解析验证和用户信息的获取。
总结
良好的 API 接口设计可以提高服务的开放性和易用性。而用户认证是保证 API 安全使用的重要手段。本文介绍了设计准则和认证实现方案,可以作为开放服务的参考。