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

42 阅读3分钟

API是什么

API ( Application Programming Interface ,应用程序编程接口)是一些预先定义的函数或者接口,目的是提供应用 程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无须访问源码,或理解内部工作机制的细节。 要实现一个 API 服务器,首先要考虑两个方面: API 风格和媒体类型 。 Go 语言中常用的 API 风格是 RPC 和 REST ,常用的媒体类型是 JSON 、 XML 和 Protobuf 。 在 Go API 开发中常用的组合是 gRPC + Protobuf 和 REST + JSON 。

REST简介

REST 代表表现层状态转移( REpresentational State Transfer ),由 Roy Fielding 在他的论文中提出。 REST 是一 种软件架构风格,不是技术框架,REST 有一系列规范,满足这些规范的 API 均可称为 RESTful API 。 REST 规范中有 如下几个核心:

  1. REST 中一切实体都被抽象成资源,每个资源有一个唯一的标识 —— URI ,所有的行为都应该是在资源上的 CRUD 操作
  2. 使用标准的方法 (GET/POST/PUT/DELETE) 来更改资源的状态,常见的操作有:资源的增删改查操作
  3. 无状态:这里的无状态是指每个 RESTful API 请求都包含了所有足够完成本次操作的信息,服务器端无须保持 Session 无状态对于服务端的弹性扩容是很重要的。 REST 风格虽然适用于很多传输协议,但在实际开发中, REST 由于天生和 HTTP 协议相辅相成,因此 HTTP 协议已经 成了实现 RESTful API 事实上的标准。在 HTTP 协议中通过 POST 、 DELETE 、 PUT 、 GET 方法来对应 REST 资源的

构建API接口

goCopy code
package main

import (
    "fmt"
    "net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello")
}

func main() {
    http.HandleFunc("/hello", helloHandler)
    http.ListenAndServe(":8080", nil)
}

用户认证

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

完整代码:

package router

import (
	"jwt-go"
	"gin"
	"time"
)

type LoginInfo struct {
	UserName string  `json:"user_name"`
	Password string `json:"password"`
}


type JWTClaims struct { // token里面添加用户信息,验证token后可能会用到用户信息
	jwt.StandardClaims
	UserID      int      `json:"user_id"`
	Password    string   `json:"password"`
	Username    string   `json:"username"`
	FullName    string   `json:"full_name"`
	Permissions []string `json:"permissions"`
}

var (
	Secret     = "zyc" // 加盐
	ExpireTime = 3600*24        // token有效期
)

//登录
func Login(c *gin.Context) {

	var loginRequest LoginInfo
	err := c.Bind(&loginRequest)
	if err != nil {
		c.JSON(400,err.Error())
		return
	}
	//模拟用户认证
	if loginRequest.UserName != "zyc" || loginRequest.Password != "zycqsn" {
		c.JSON(401,gin.H{"err":"账号或密码错误"})
		return
	}

	claims := &JWTClaims{
		UserID:      1,
		Username:    loginRequest.UserName,
		Password:    loginRequest.Password,
		FullName:    loginRequest.UserName,
		Permissions: []string{},
	}
	claims.IssuedAt = time.Now().Unix()
	claims.ExpiresAt = time.Now().Add(time.Second * time.Duration(ExpireTime)).Unix()
	signedToken,err:=getToken(claims)

	if err!=nil {
		c.JSON(400,err.Error())
		return
	}
	//fmt.Printf(signedToken)
	c.JSON(200, gin.H{"token": signedToken})
}


//身份验证

func Verify(c *gin.Context)(result bool,userName string,err error) {
	strToken,err := c.Cookie("Crow")
	if strToken == ""{
		result = false
		return result,"",nil
	}
	claim,err := verifyAction(strToken)
	if err != nil {
		result = false
		return result,"",nil
	}
	result = true
	userName = claim.Username
	return result,userName,nil
}

//刷新token
func Refresh(c *gin.Context) {
	strToken,err := c.Cookie("Crow")
	claims,err := verifyAction(strToken)
	if err != nil {
		c.JSON(400,gin.H{"err":err.Error()})
		return
	}
	claims.ExpiresAt = time.Now().Unix() + (claims.ExpiresAt - claims.IssuedAt)
	signedToken,err:=getToken(claims)
	if err!=nil{
		c.JSON(400,gin.H{"err":err.Error()})
		return
	}
	c.JSON(200,gin.H{"data": signedToken })
}

//验证token是否存在,存在则获取信息
func verifyAction(strToken string) (claims *JWTClaims,err error) {
	token, err := jwt.ParseWithClaims(strToken, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(Secret), nil
	})
	if err != nil {
		return nil, err
	}
	claims, ok := token.Claims.(*JWTClaims)
	if !ok {
		return nil, err
	}
	if err := token.Claims.Valid(); err != nil {
		return nil, err
	}
	return claims, nil
}

//生成token
func getToken(claims *JWTClaims)(signedToken string,err error){
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signedToken, err = token.SignedString([]byte(Secret))
	if err != nil {
		return "",err
	}
	return signedToken,nil
}