从4开始,在后端系统中增加用户注册和登录功能

简介: 从4开始,在后端系统中增加用户注册和登录功能

本次我们接着上四篇文章进行讲解《从0开始,用Go语言搭建一个简单的后端业务系统》《从1开始,扩展Go语言后端业务系统的RPC功能》《从2开始,在Go语言后端业务系统中引入缓存》以及《从3开始,在业务系统中增加分页功能》,这次是系统中比较核心的功能——用户登录&注册,这个功能其实本应该是最先实现的,但是由于不同因素的影响,放到了本次进行实现,不过也无伤大雅,后期我们都会不断的进行查漏补缺和优化来使我们的项目总体上更加优雅,话不多说,我们开始正文:

1 用户注册&登录流程

(1)注册流程

(2)登录流程

2 代码实现

user结构:

package model
import (
   "encoding/json"
)
type User struct {
   Id         int64  `json:"id"`
   Name       string `json:"name"`
   LoginName  string `json:"login_name"`
   Role       int64  `json:"role"`
   Pwd        string `json:"pwd"`
   CreateTime string `json:"create_time"`
}
func (user User) TableName() string {
   return "user_info"
}
func (user User) MarshalJSON() ([]byte, error) {
   return json.Marshal(map[string]interface{}{
      "id":          user.Id,
      "name":        user.Name,
      "login_name":  user.LoginName,
      "role":        user.Role,
      "pwd":         user.Pwd,
      "create_time": user.CreateTime,
   })
}
//Redis类似序列化操作
func (user User) MarshalBinary() ([]byte, error) {
   return json.Marshal(user)
}
func (user User) UnmarshalBinary(data []byte) error {
   return json.Unmarshal(data, &user)
}

dao层代码:

package dao
import (
   "context"
   "count_num/pkg/model"
)
type UserDao interface {
   // 添加一个
   CreateUser(ctx context.Context, user model.User) bool
   // 根据ID查找一个
   GetUserByUid(ctx context.Context, uId int64) model.User
   // 查找全部
   GetAll(ctx context.Context, page int, limit int) []model.User
   // 根据ID修改一个
   UpdateUserById(ctx context.Context, user model.User) bool
   // 根据登录名查找一个
   GetUserByLoginName(ctx context.Context, loginName string) model.User
}

dao层实现:

package impl
import (
   "context"
   "count_num/pkg/cache"
   "count_num/pkg/config"
   "count_num/pkg/model"
   "count_num/pkg/utils"
   "gorm.io/gorm"
)
type UserDaoImpl struct {
   db    *gorm.DB
   cache *cache.CountNumCacheDAOImpl
}
func NewUserDaoImpl() *UserDaoImpl {
   return &UserDaoImpl{db: config.DB, cache: cache.NewCountNumCacheDAOImpl()}
}
func (impl *UserDaoImpl) CreateUser(ctx context.Context, user model.User) bool {
   var u model.User
   impl.db.First(&u, "login_name", user.LoginName)
   if u.LoginName == user.LoginName {
      return false
   }
   user.Pwd = utils.GetMd5Str(user.Pwd)
   user.CreateTime = utils.NowTimeStr()
   impl.db.Save(&user)
   return true
}
func (impl *UserDaoImpl) GetUserByUid(ctx context.Context, uId int64) model.User {
   var user model.User
   impl.db.First(&user, "id", uId)
   return user
}
func (impl *UserDaoImpl) GetAll(ctx context.Context, page int, limit int) []model.User {
   users := make([]model.User, 0)
   if page <= 0 || limit <= 0 {
      impl.db.Find(&users)
   } else {
      impl.db.Limit(limit).Offset((page - 1) * limit).Find(&users)
   }
   return users
}
func (impl *UserDaoImpl) UpdateUserById(ctx context.Context, user model.User) bool {
   impl.db.Model(&model.User{}).Where("id = ?", user.Id).Updates(user)
   return true
}
func (impl *UserDaoImpl) GetUserByLoginName(ctx context.Context, loginName string) model.User {
   var user model.User
   impl.db.First(&user, "login_name", loginName)
   return user
}

工具方法,用于生成token和MD5加密:

package utils
import (
   "bytes"
   "crypto/md5"
   "encoding/gob"
   "encoding/hex"
   "math/rand"
   "strings"
   "time"
)
func GetMd5Str(str string) string {
   md5 := md5.New()
   var buf bytes.Buffer
   gob.NewEncoder(&buf).Encode(str)
   md5.Write(buf.Bytes())
   return hex.EncodeToString(md5.Sum(nil))
}
func GetTokenStr() string {
   char := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
   charArr := strings.Split(char, "")
   c := len(charArr)
   ran := rand.New(rand.NewSource(time.Now().Unix()))
   var str string = ""
   for i := 1; i <= 18; i++ {
      str = str + charArr[ran.Intn(c)]
   }
   return str
}

工具方法:

package utils
import (
   "time"
)
// NowTimeStr return 2022-01-21 12:21:31
func NowTimeStr() string {
   return time.Unix(time.Now().Unix(), 0).Format("2006-01-02 15:04:05")
}
// NowTimeStamp return 1657255820
func NowTimeStamp() int64 {
   return time.Now().Unix()
}
// TimeStamp2NowTimeStr  1657255820 -> 2022-01-21 12:21:31
func TimeStamp2NowTimeStr(stamp int64) string {
   format := time.Unix(stamp, 0).Format("2006-01-02 15:04:05")
   return format
}
// NowTimeStr2TimeStamp  2022-01-21 12:21:31 -> 1657255820
func NowTimeStr2TimeStamp(str string) int64 {
   var LOC, _ = time.LoadLocation("Asia/Shanghai")
   tim, _ := time.ParseInLocation("2006-01-02 15:04:05", str, LOC)
   return tim.Unix()
}

controller层:

package controller
import (
   "count_num/pkg/dao/impl"
   "count_num/pkg/model"
   "count_num/pkg/utils"
   "count_num/pkg/web/auth"
   "encoding/json"
   "github.com/gin-gonic/gin"
   "io/ioutil"
)
type UserControllerImpl struct {
   dao *impl.UserDaoImpl
}
type UserController interface {
   CreateUser(c *gin.Context)
   FindUserByLoginNameAndPwd(c *gin.Context)
   Register(c *gin.Context)
}
func NewUserController() *UserControllerImpl {
   return &UserControllerImpl{dao: impl.NewUserDaoImpl()}
}
func (impl UserControllerImpl) CreateUser(c *gin.Context) {
   body := c.Request.Body
   bytes, err := ioutil.ReadAll(body)
   user := model.User{}
   json.Unmarshal(bytes, &user)
   if err != nil {
      panic(err)
   }
   res := impl.dao.CreateUser(c, user)
   c.JSON(200, map[string]interface{}{"code": 0, "msg": "", "count": 0, "data": res})
}
func (impl UserControllerImpl) FindUserByLoginNameAndPwd(c *gin.Context) {
   body := c.Request.Body
   bytes, err := ioutil.ReadAll(body)
   user := model.User{}
   json.Unmarshal(bytes, &user)
   if err != nil {
      panic(err)
   }
   userByLoginName := impl.dao.GetUserByLoginName(c, user.LoginName)
   //密码通过
   if userByLoginName.Pwd == utils.GetMd5Str(user.Pwd) {
      setToken := auth.SetToken(c, utils.GetTokenStr(), user)
      c.JSON(200, map[string]interface{}{"code": 0, "msg": setToken, "count": 0, "data": utils.GetTokenStr()})
   } else {
      if userByLoginName.Id == 0 {
         c.JSON(200, map[string]interface{}{"code": 0, "msg": "账号不存在", "count": 0, "data": "-1"})
      } else {
         c.JSON(200, map[string]interface{}{"code": 0, "msg": "密码错误", "count": 0, "data": "-1"})
      }
   }
}
func (impl UserControllerImpl) Register(c *gin.Context) {
   body := c.Request.Body
   bytes, err := ioutil.ReadAll(body)
   user := model.User{}
   json.Unmarshal(bytes, &user)
   if err != nil {
      panic(err)
   }
   user.Role = 1
   res := impl.dao.CreateUser(c, user)
   c.JSON(200, map[string]interface{}{"code": 0, "msg": "", "count": 0, "data": res})
}

router增加URL:

......
userInfo := r.Group("/user")
{
   userInfo.POST("/save", controller.NewUserController().CreateUser)
   userInfo.POST("/login", controller.NewUserController().FindUserByLoginNameAndPwd)
   userInfo.POST("/register", controller.NewUserController().Register)
}
......

前端代码:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>登录/注册</title>
    <link rel="stylesheet" href="./layui/css/layui.css">
    <style>
        #form {
            width: 30%;
            margin-left: 35%;
            margin-top: 200px;
            padding: 30px;
            border: 3px solid #c5c5b0;
            border-radius: 20px;
        }
        #form2 {
            width: 30%;
            margin-left: 35%;
            margin-top: 200px;
            padding: 30px;
            border: 3px solid #c5c5b0;
            border-radius: 20px;
        }
        .hidden {
            display: none;
        }
        .show {
            display: block;
        }
    </style>
</head>
<body>
<form class="layui-form" id="form">
    <h3 style="font-size: 20px;text-align: center;margin-bottom: 30px;">登录</h3>
    <div class="layui-form-item">
        <label class="layui-form-label">账号</label>
        <div class="layui-input-inline">
            <input type="text" id="loginName" placeholder="请输入账号" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">密码</label>
        <div class="layui-input-inline">
            <input type="password" id="loginPwd" placeholder="请输入密码" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-input-block">
            <button class="layui-btn" type="button" onclick="login()">立即提交</button>
            <button type="button" onclick="toRegister()" class="layui-btn layui-btn-primary">注册</button>
        </div>
    </div>
</form>
<form class="layui-form hidden" id="form2">
    <h3 style="font-size: 20px;text-align: center;margin-bottom: 30px;">注册</h3>
    <div class="layui-form-item">
        <label class="layui-form-label">昵称</label>
        <div class="layui-input-inline">
            <input type="text" id="regName" placeholder="请输入昵称" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">账号</label>
        <div class="layui-input-inline">
            <input type="text" id="regLoginName" placeholder="请输入账号" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">密码</label>
        <div class="layui-input-inline">
            <input type="password" id="regPwd" placeholder="请输入密码" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">确认密码</label>
        <div class="layui-input-inline">
            <input type="password" id="regPwd2" placeholder="请再次输入密码"
                   autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">身份</label>
        <div class="layui-input-block">
            <input type="radio" id="role" value="user" title="用户" checked>
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-input-block">
            <button class="layui-btn" type="button" onclick="register()">立即提交</button>
            <button type="button" onclick="toLogin()" class="layui-btn layui-btn-primary">登录</button>
        </div>
    </div>
</form>
</body>
<script src="./layui/layui.js"></script>
<script src="./layui/jquery.min.js"></script>
<script>
    var base_url = 'http://localhost:9888'
    function login() {
        var loginName = $("#loginName").val()
        var loginPwd = $("#loginPwd").val()
        var data = {
            'login_name': loginName,
            'pwd': loginPwd
        }
        $.ajax({
            url: base_url + "/user/login",
            type: "POST",
            data: JSON.stringify(data),
            success: function (res) {
                if (res.data.length >= 10) {
                    localStorage.setItem("token", res.data)
                    return;
                }
                alert(res.msg)
                return
            },
            error: function (err) {
                alert(err)
                return
            }
        })
    }
    function register() {
        var regName = $("#regName").val()
        var regLoginName = $("#regLoginName").val()
        var regPwd = $("#regPwd").val()
        var regPwd2 = $("#regPwd2").val()
        if (regPwd != regPwd2) {
            alert("密码不一致鸭")
            return
        }
        var data = {
            'name': regName,
            'login_name': regLoginName,
            'pwd': regPwd
        }
        $.ajax({
            url: base_url + "/user/register",
            type: "POST",
            data: JSON.stringify(data),
            success: function (res) {
                if (res.data) {
                    alert("注册成功")
                } else {
                    alert("注册失败")
                }
            },
            error: function (err) {
                console.log(err)
            }
        })
    }
    function toLogin() {
        $("#form2").addClass("hidden")
        $("#form").removeClass("hidden")
    }
    function toRegister() {
        $("#form").addClass("hidden")
        $("#form2").removeClass("hidden")
    }
</script>
</html>

3 小结

用户的登录和注册功能在一般情况下会使用到验证码,所以这里准备了一篇文章:一文搞懂Go整合captcha实现验证码功能,大家可以自行设计和补充哈。

除此之外,在系统的注册&登录功能背后,往往都会有系统的认证和授权,所以请大家耐心等待我的下一篇文章!

相关文章
|
2月前
|
JSON Go 数据格式
从1开始,扩展Go语言后端业务系统的RPC功能
从1开始,扩展Go语言后端业务系统的RPC功能
32 0
|
1月前
|
人工智能 运维 监控
构建高性能微服务架构:现代后端开发的挑战与策略构建高效自动化运维系统的关键策略
【2月更文挑战第30天】 随着企业应用的复杂性增加,传统的单体应用架构已经难以满足快速迭代和高可用性的需求。微服务架构作为解决方案,以其服务的细粒度、独立性和弹性而受到青睐。本文将深入探讨如何构建一个高性能的微服务系统,包括关键的设计原则、常用的技术栈选择以及性能优化的最佳实践。我们将分析微服务在处理分布式事务、数据一致性以及服务发现等方面的挑战,并提出相应的解决策略。通过实例分析和案例研究,我们的目标是为后端开发人员提供一套实用的指南,帮助他们构建出既能快速响应市场变化,又能保持高效率和稳定性的微服务系统。 【2月更文挑战第30天】随着信息技术的飞速发展,企业对于信息系统的稳定性和效率要求
|
1天前
|
安全 关系型数据库 MySQL
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
10 3
|
16天前
|
JavaScript 前端开发 关系型数据库
旅游规划助手:结合Vue的交云性设计和Python的强大后端功能
【4月更文挑战第11天】本文探讨了如何使用Vue.js和Python(Flask或Django)构建旅游规划助手应用,简化旅行规划。首先,确保安装了Python、Node.js、数据库系统和Git。接着,介绍如何用Python搭建后端API,分别展示了Flask和Django的例子。然后,利用Vue.js初始化前端项目,结合Vuex和Vue Router构建用户界面。最后,通过Axios实现前端与后端的数据通信。这样的架构有利于团队协作和代码维护,便于扩展应用功能。
|
1月前
|
数据采集 JavaScript API
第三方系统访问微搭低代码的后端API
第三方系统访问微搭低代码的后端API
|
2月前
|
缓存 NoSQL Go
从2开始,在Go语言后端业务系统中引入缓存
从2开始,在Go语言后端业务系统中引入缓存
31 0
|
18天前
|
前端开发 JavaScript 关系型数据库
从前端到后端:构建现代化Web应用的技术探索
在当今互联网时代,Web应用的开发已成为了各行各业不可或缺的一部分。从前端到后端,这篇文章将带你深入探索如何构建现代化的Web应用。我们将介绍多种技术,包括前端开发、后端开发以及各种编程语言(如Java、Python、C、PHP、Go)和数据库,帮助你了解如何利用这些技术构建出高效、安全和可扩展的Web应用。
|
19天前
|
前端开发 小程序 Java
uniapp上传图片 前端以及java后端代码实现
uniapp上传图片 前端以及java后端代码实现
33 0
|
1月前
|
JSON 前端开发 Java
layui上传图片,前端直接拷代码,后端……
layui上传图片,前端直接拷代码,后端……
30 0
|
1月前
|
前端开发
前端接受后端文件流并下载到本地的方法
前端接受后端文件流并下载到本地的方法
60 0