Gin 搭建注册接口 | Go主题月

770 阅读2分钟

注册接口

  • 检查提交数据完整,必填字段必须填写
  • 检查提交的数据是否存在(用户名、手机号码)
  • 提交密码敏感信息加密
  • id 使用雪花算法生成
  • 密码二次确认,比对密码是否一致
  • 表设计用户信息和密码分别存在不同表中,使用用户 userId 关联密码表
  • 需要发送短信验证码,提交信息服务端校验验证码是否合法
  • 由于接口不需要授权就能访问,需要拦截频繁访问
  • ....

添加一个注册路由

account_controller.go

type AccountController struct{}

func (ac *AccountController) RegisterRouter(app *gin.Engine) {
	group := app.Group("/api/v1/account")
	group.POST("/register", ac.Register)
	
	// TODO: login interface 
	group.POST("/login", ac.Login)
}

func (ac *AccountController) Register(c *gin.Context) {
	var registerInfo model.RegisterInfo
	if err := c.BindJSON(&registerInfo); err != nil {
		common.Failed(c, api.ParseParamCode, err.Error())
		return
	}
	log.Printf("receiver param %s", registerInfo)
	accountService := service.AccountService{}
	accountService.AddUser(&registerInfo)
	common.Success(c, 1)
}
  • 提交数据为JSON格式数据
  • 使用 gin 提供数据绑定 API BindJSON,绑定映射数据到 RegisterInfo 结构体
  • 通过 AccountService 提供 AddUser 方法进行业务逻辑处理

register_info.go

package model

type RegisterInfo struct {
	UserName    string `form:"userName" json:"userName" binding:"required"`
	Password    string `form:"password" json:"password" binding:"required"`
	RePassword  string `form:"rePassword" json:"rePassword" binding:"required"`
	Phone       string `form:"phone" json:"phone" binding:"required"`
	SmsCode     string `form:"smsCode" json:"smsCode" binding:"required"`
	Gender      string `form:"gender" json:"gender"`
	Avatar      string `form:"avatar" json:"avatar"`
	Email       string `form:"email" json:"email"`
	Job         string `form:"job" json:"job"`
	Address     string `form:"address" json:"address"`
	Description string `form:"description" json:"description"`
}

  • 定义 RegisterInfo 结构体
  • 使用 form、binding( tag )进行标记提交数据,一些必须填写的数据使用 binding:"required",在提交到接口时,BindJSON 方法会进行校验。

account_service.go

type AccountService struct{}

func (us *AccountService) AddUser(registerInfo *model.RegisterInfo) {
	password := registerInfo.Password
	repassword := registerInfo.RePassword

	if !strings.EqualFold(password, repassword) {
		api.NewException(api.PasswordInconsistencyCode)
	}

	worker, err := util.NewWorker(16)
	if err != nil {
		log.Print(err)
		api.NewException(api.AddUserFailCode)
	}

	userDao := dao.UserDao{DbEngine: engine.GetOrmEngine()}

	// check user info
	checkUserInfo(registerInfo)

	userInfo := &model.UserInfo{
		UserName:    registerInfo.UserName,
		UserId:      strconv.FormatInt(worker.GetId(), 10),
		Gender:      registerInfo.Gender,
		Phone:       registerInfo.Phone,
		Avatar:      registerInfo.Avatar,
		Email:       registerInfo.Email,
		Address:     registerInfo.Address,
		Description: registerInfo.Description,
	}
	result, err := userDao.InsertUser(userInfo)
	log.Printf("add user result %d error %s", result, err)
	if err != nil {
		panic(api.NewException(api.AddUserFailCode))
	}

	passwordInfo := &model.Password{
		PwdId:  strconv.FormatInt(worker.GetId(), 10),
		UserId: userInfo.UserId,
		Pwd:    util.CryptoSha1(password),
	}
	result, err = userDao.InsertPassword(passwordInfo)
	log.Printf("add password result %d error %s", result, err)
	if err != nil {
		panic(api.NewException(api.AddUserFailCode))
	}
}

func checkUserInfo(registerInfo *model.RegisterInfo) {
	userDao := dao.UserDao{DbEngine: engine.GetOrmEngine()}
	exist, err := userDao.QueryByPhone(registerInfo.Phone)
	log.Printf("check Phone %v %v", exist, err)
	if exist {
		panic(api.NewException(api.PhoneExistCode))
	}

	exist, err = userDao.QueryByUserName(registerInfo.UserName)
	log.Printf("check UserName %v %v", exist, err)
	if exist {
		panic(api.NewException(api.UserNameExistCode))
	}
}
  • strings.EqualFold(password, repassword) 进行密码比对
  • 使用 worker, err := util.NewWorker(16) 生成雪花 id
  • util.CryptoSha1 使用 sha1 算法进行密码加密
  • 从数据库 chckUserInfo 检查用户信息
  • 检查完数据,进行数据落地,把用户信息和密码分别保存到数据库

password.go

package model

type Password struct {
	PwdId  string `xorm:"pwd_id"`
	UserId string `xorm:"user_id"`
	Pwd    string `xorm:"pwd"`
}

  • user_id 关联用户表

密码加密

  • 在 go 语言中,提供了很多加密算法,可以进行加密

image-20210331232033483

  • Sha1 加密
package util

import (
	"crypto/sha1"

	"encoding/hex"
)

func CryptoSha1(s string) string {
	s1 := sha1.New()
	s1.Write([]byte(s))
	return hex.EncodeToString(s1.Sum(nil))
}

TODO:

发送短信

  • 可以使用第三方短信服务,通过 API 发送短信,将短信验证码,存入 Redis 中,设置过期时间,XXX 分钟内有效
  • 从提交数据中获取短信验证码和手机号码,从 Reids 中获取保存到验证码进行比对是否一致

接口限流

  • 可以使用 gin 中间件针对获取 IP 方式进行限流
  • 项目部署到服务器,使用 Nginx 配置 ngx_http_limit_req_module