注册接口
- 检查提交数据完整,必填字段必须填写
- 检查提交的数据是否存在(用户名、手机号码)
- 提交密码敏感信息加密
- 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(®isterInfo); err != nil {
common.Failed(c, api.ParseParamCode, err.Error())
return
}
log.Printf("receiver param %s", registerInfo)
accountService := service.AccountService{}
accountService.AddUser(®isterInfo)
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 语言中,提供了很多加密算法,可以进行加密
- 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