• 从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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    工具方法,用于生成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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    工具方法:

    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()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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})
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    router增加URL:

    ......
    userInfo := r.Group("/user")
    {
       userInfo.POST("/save", controller.NewUserController().CreateUser)
       userInfo.POST("/login", controller.NewUserController().FindUserByLoginNameAndPwd)
       userInfo.POST("/register", controller.NewUserController().Register)
    }
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    前端代码:

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181

    3 小结

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

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

  • 相关阅读:
    【附源码】计算机毕业设计java装修服务分析系统设计与实现
    es6新增-Promise详解(异步编程的解决方案1)
    (自用)learnOpenGL学习总结-高级光照-blinn-phong and Gamma矫正
    一夜爆火的《羊了个羊》,开发竟这么简单!
    Python语句
    Matlab图像处理- 车牌图像数据特征分析
    垃圾回收器与内存分配策略
    2022最新IntellJ IDEA诺依开发部署文档
    MySQL 崩溃恢复过程分析
    SCI一区 | Matlab实现PSO-TCN-LSTM-Attention粒子群算法优化时间卷积长短期记忆神经网络融合注意力机制多变量时间序列预测
  • 原文地址:https://blog.csdn.net/Mr_YanMingXin/article/details/125904753