Go: IM系统基于xorm实现简单的注册和登录功能

概述

  • IM系统允许用户通过网络实时发送和接收消息
  • 它通常包括用户注册、登录、消息发送、接收、存储以及用户状态管理等核心功能
  • 其中,登录功能是用户访问IM服务的第一步,它确保了系统的安全性和用户数据的准确性

基于MVC的目录设计

im-project
├── go.mod
├── main.go          主程序
├── model            模型层
│     └── user.go
├── views            模板层
│     └── user
│           ├── register.html
│           └── login.html
├── ctrl             控制器层
│     └── user.go
├── service          服务包
│     ├── init.go
│     └── user.go
├── utils            工具包
│     ├── md5.go
│     └── resp.go

主程序

在 main.go 注册模板,设置静态服务,绑定路由和控制器

package main

import (
	"net/http"
	"im-project/ctrl"
	"log"
	"html/template"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

// 注册模板
func RegisterTemplate() {
	//全局扫描模板
	GlobTemplete := template.New("root")
	GlobTemplete, err := GlobTemplete.ParseGlob("view/**/*")
	if err!=nil {
		//打印错误信息
		//退出系统
		log.Fatal(err)
	}
	//分别对每一个模板进行注册
	for _,tpl := range  GlobTemplete.Templates(){
		patern := tpl.Name()
		http.HandleFunc(patern,
			func(w http.ResponseWriter,
			r *http.Request) {
			GlobTemplete.ExecuteTemplate(w,patern,nil)
		})
		// fmt.Println("register=>"+patern)
	}
}

// 这里注册视图,仅供参考
func RegisterView(){
	//一次解析出全部模板
	tpl,err := template.ParseGlob("view/**/*")
	if nil!=err{
		log.Fatal(err)
	}
	//通过for循环做好映射
	for _,v := range tpl.Templates(){
		tplname := v.Name();
		http.HandleFunc(tplname, func(w http.ResponseWriter,
			request *http.Request) {
				// fmt.Println("parse     "+v.Name() + "==" + tplname)
				err := tpl.ExecuteTemplate(w,tplname,nil)
				if err!=nil {
					log.Fatal(err.Error())
				}
		})
	}
}

func main() {
	// 1. 路由绑定请求和控制器处理函数
	http.HandleFunc("/user/login", ctrl.UserLogin)
	http.HandleFunc("/user/register", ctrl.UserRegister)
	// 2. 指定目录的静态文件
	http.Handle("/asset/",http.FileServer(http.Dir(".")))
	http.Handle("/mnt/",http.FileServer(http.Dir(".")))
	// 3. 注册模板
	RegisterView()
	// 4. 运行
	http.ListenAndServe(":8080",nil)
}

模型层

定义user实体

package model

import "time"

const (
	SEX_WOMEN = "W"
	SEX_MEN = "M"
	SEX_UNKNOW = "U"
)

type User struct {
	//用户ID
	Id         int64     `xorm:"pk autoincr bigint(20)" form:"id" json:"id"`
	Mobile   string 		`xorm:"varchar(20)" form:"mobile" json:"mobile"`
	Passwd       string	`xorm:"varchar(40)" form:"passwd" json:"-"`   // 什么角色
	Avatar	   string 		`xorm:"varchar(150)" form:"avatar" json:"avatar"`
	Sex        string	`xorm:"varchar(2)" form:"sex" json:"sex"`   // 什么角色
	Nickname    string	`xorm:"varchar(20)" form:"nickname" json:"nickname"`   // 什么角色
	//加盐随机字符串6
	Salt       string	`xorm:"varchar(10)" form:"salt" json:"-"`   // 什么角色
	Online     int	`xorm:"int(10)" form:"online" json:"online"`   //是否在线
	//前端鉴权因子,
	Token      string	`xorm:"varchar(40)" form:"token" json:"token"`   // 什么角色
	Memo      string	`xorm:"varchar(140)" form:"memo" json:"memo"`   // 什么角色
	Createat   time.Time	`xorm:"datetime" form:"createat" json:"createat"`   // 什么角色
}

服务包

init.go 用于初始化数据库相关操作

package service

import (
	"github.com/go-xorm/xorm"
	"log"
	"fmt"
	"errors"
	"im-project/model"
)

var DbEngin *xorm.Engine

func  init()  {
	drivename :="mysql"
	DsName := "root:root@(192.168.0.102:3306)/chat?charset=utf8"
	err := errors.New("")
	DbEngin,err = xorm.NewEngine(drivename,DsName)
	if nil!=err && ""!=err.Error() {
		log.Fatal(err.Error())
	}
	//是否显示SQL语句
	DbEngin.ShowSQL(false)
	//数据库最大打开的连接数
	DbEngin.SetMaxOpenConns(2)

	//自动User
	DbEngin.Sync2(new(model.User))
	// DbEngin = dbengin
	fmt.Println("init data base ok")
}

user.go 登录时用到的封装函数

package service

import (
	"im-project/model"
	"errors"
	"fmt"
	"math/rand"
	"im-project/util"
	"time"
	_ "github.com/go-sql-driver/mysql"
)

type UserService struct {}

//注册函数
func (s *UserService)Register(
	mobile,//手机
	plainpwd,//明文密码
	nickname,//昵称
	avatar,sex string)(user model.User,err error) {

	//检测手机号码是否存在,
	tmp := model.User{}
	_,err=DbEngin.Where("mobile=? ",mobile).Get(&tmp)
	if err!=nil{
		return tmp,err
	}
	//如果存在则返回提示已经注册
    if tmp.Id>0{
    	return tmp,errors.New("该手机号已经注册")
	}
	//否则拼接插入数据
	tmp.Mobile = mobile
	tmp.Avatar = avatar
	tmp.Nickname = nickname
	tmp.Sex = sex
	tmp.Salt = fmt.Sprintf("%06d",rand.Int31n(10000))
	tmp.Passwd = util.MakePasswd(plainpwd,tmp.Salt)
	tmp.Createat = time.Now()
	//token 可以是一个随机数
	tmp.Token = fmt.Sprintf("%08d",rand.Int31())
	//passwd =
	//md5 加密
	//返回新用户信息

	//插入 InserOne
	_,err = DbEngin.InsertOne(&tmp)
	//前端恶意插入特殊字符
	//数据库连接操作失败
	return tmp,err
}

//登录函数
func (s *UserService)Login(mobile, plainpwd string )(user model.User,err error) {
    //首先通过手机号查询用户
    tmp := model.User{}
	DbEngin.Where("mobile = ?",mobile).Get(&tmp)
    // 如果没有找到
    if tmp.Id==0 {
    	return tmp,errors.New("该用户不存在")
	}
	// 查询到了比对密码
	if !util.ValidatePasswd(plainpwd,tmp.Salt,tmp.Passwd){
		return tmp,errors.New("密码不正确")
	}
    // 刷新token, 安全
	str := fmt.Sprintf("%d",time.Now().Unix())
	token := util.MD5Encode(str)
	tmp.Token = token
    //返回数据
    DbEngin.ID(tmp.Id).Cols("token").Update(&tmp)
    return tmp,nil
}

//查找某个用户
func (s *UserService)Find(
	userId int64 )(user model.User) {

	//首先通过手机号查询用户
	tmp :=model.User{}
	DbEngin.ID(userId).Get(&tmp)
	return tmp
}

工具包

util/md5.go

package util

import (
	"crypto/md5"
	"encoding/hex"
	"strings"
)
//小写的
func Md5Encode(data string) string{
	h := md5.New()
	h.Write([]byte(data)) // 需要加密的字符串为 123456
	cipherStr := h.Sum(nil)
	return  hex.EncodeToString(cipherStr)
}
//大写
func MD5Encode(data string) string{
	return strings.ToUpper(Md5Encode(data))
}

func ValidatePasswd(plainpwd,salt,passwd string) bool{
	return Md5Encode(plainpwd+salt)==passwd
}
func MakePasswd(plainpwd,salt string) string{
	return Md5Encode(plainpwd+salt)
}

util/resp.go

package util

import (
	"net/http"
	"encoding/json"
	"log"
)

type H struct {
	Code int `json:"code"`
	Msg  string `json:"msg"`
	Data interface{} `json:"data,omitempty"`
	Rows interface{} `json:"rows,omitempty"`
	Total interface{} `json:"total,omitempty"`
}
//
func RespFail(w http.ResponseWriter,msg string){
	Resp(w,-1,nil,msg)
}
func RespOk(w http.ResponseWriter,data interface{},msg string){
	Resp(w,0,data,msg)
}
func RespOkList(w http.ResponseWriter,lists interface{},total interface{}){
	//分页数目,
	RespList(w,0,lists,total)
}
func Resp(w http.ResponseWriter,code int,data interface{},msg string)  {

	w.Header().Set("Content-Type","application/json")
	//设置200状态
	w.WriteHeader(http.StatusOK)
	//输出
	//定义一个结构体
	h := H{
		Code:code,
		Msg:msg,
		Data:data,
	}
	//将结构体转化成JSOn字符串
	ret,err := json.Marshal(h)
	if err!=nil{
		log.Println(err.Error())
	}
	//输出
	w.Write(ret)
}
func RespList(w http.ResponseWriter,code int,data interface{},total interface{})  {

	w.Header().Set("Content-Type","application/json")
	//设置200状态
	w.WriteHeader(http.StatusOK)
	//输出
	//定义一个结构体
	//满足某一条件的全部记录数目
	//测试 100
	//20
 	h := H{
		Code:code,
		Rows:data,
		Total:total,
	}
	//将结构体转化成JSOn字符串
	ret,err := json.Marshal(h)
	if err!=nil{
		log.Println(err.Error())
	}
	//输出
	w.Write(ret)
}

控制器

ctrl/user.go 中用于处理登录时的接口响应

package ctrl

import (
	"net/http"
	"fmt"
	"math/rand"
	"im-project/util"
	"im-project/service"
	"im-project/model"
)

func UserLogin(writer http.ResponseWriter, request *http.Request) {
	// 数据库操作
	// 逻辑处理
	// restapi json/xml返回
	// 1.获取前端传递的参数
	// mobile,passwd
	// 解析参数
	// 如何获得参数
	// 解析参数
	request.ParseForm()
	mobile := request.PostForm.Get("mobile")
	passwd := request.PostForm.Get("passwd")

    //模拟
    user,err := userService.Login(mobile,passwd)
    if err!=nil {
    	util.RespFail(writer,err.Error())
	} else {
		util.RespOk(writer,user,"")
	}
}

var userService service.UserService

func UserRegister(writer http.ResponseWriter, request *http.Request) {
	request.ParseForm()
	mobile := request.PostForm.Get("mobile")
	plainpwd := request.PostForm.Get("passwd")
	nickname := fmt.Sprintf("user%06d",rand.Int31())
	avatar := ""
	sex := model.SEX_UNKNOW

	user,err := userService.Register(mobile, plainpwd, nickname, avatar, sex)
	if err != nil {
		util.RespFail(writer,err.Error())
	} else {
		util.RespOk(writer,user,"")
	}
}

模板层

view/user/login.html 登录页面

{{define  "/user/login.shtml"}}
<!DOCTYPE html>
<html>
<head>
{{template "/chat/head.shtml"}}
</head>
<body>

<header class="mui-bar mui-bar-nav">
    <h1 class="mui-title">登录</h1>
</header>
<div class="mui-content" id="pageapp">
    <form id='login-form' class="mui-input-group">
        <div class="mui-input-row">
            <label>账号</label>
            <input v-model="user.mobile" placeholder="请输入手机号" type="text" class="mui-input-clear mui-input" >
        </div>
        <div class="mui-input-row">
            <label>密码</label>
            <input v-model="user.passwd" placeholder="请输入密码"  type="password" class="mui-input-clear mui-input" >
        </div>
    </form>
    <div class="mui-content-padded">
        <button @click="login"  type="button"  class="mui-btn mui-btn-block mui-btn-primary">登录</button>
        <div class="link-area"><a id='reg' href="register.shtml">注册账号</a> <span class="spliter">|</span> <a  id='forgetPassword'>忘记密码</a>
        </div>
    </div>
    <div class="mui-content-padded oauth-area">
    </div>
</div>
</body>
</html>
<script>
    var app = new Vue({
        el:"#pageapp",
        data:function(){
            return {
                user:{
                    mobile:"",
                    passwd:""
                }
            }
        },
        methods:{
            login:function(){
                //检测手机号是否正确
                //检测密码是否为空
                //网络请求
                //封装了promis
                util.post("user/login",this.user).then(res=>{
                    console.log(res)
                    if(res.code!=0) {
                        mui.toast(res.msg)
                        return
                    }
                    var url = "/chat/index.shtml?id=" + res.data.id + "&token=" + res.data.token
                    userInfo(res.data)
                    userId(res.data.id)
                    location.href = url
                })
            },
        }
    })
</script>
{{end}}

view/user/register.html 注册页面

{{define  "/user/register.shtml"}}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">
    <title>IM解决方案</title>
    <link rel="stylesheet" href="/asset/plugins/mui/css/mui.css" />
    <link rel="stylesheet" href="/asset/css/login.css" />
    <script src="/asset/plugins/mui/js/mui.js" ></script>
    <script src="/asset/js/vue.min.js" ></script>
    <script src="/asset/js/util.js" ></script>
</head>
<body>

<header class="mui-bar mui-bar-nav">
    <h1 class="mui-title">登录</h1>
</header>
<div class="mui-content" id="pageapp">
    <form id='login-form' class="mui-input-group">
        <div class="mui-input-row">
            <label>账号</label>
            <input v-model="user.mobile" placeholder="请输入手机号" type="text" class="mui-input-clear mui-input" >
        </div>
        <div class="mui-input-row">
            <label>密码</label>
            <input v-model="user.passwd" placeholder="请输入密码"  type="password" class="mui-input-clear mui-input" >
        </div>
    </form>
    <div class="mui-content-padded">
        <button @click="login"  type="button"  class="mui-btn mui-btn-block mui-btn-primary">登录</button>
        <div class="link-area"><a id='reg' href="register.shtml">注册账号</a> <span class="spliter">|</span> <a  id='forgetPassword'>忘记密码</a>
        </div>
    </div>
    <div class="mui-content-padded oauth-area">
    </div>
</div>
</body>
</html>
<script>
    var app = new Vue({
        el:"#pageapp",
        data:function(){
            return {
                user:{
                    mobile:"",
                    passwd:""
                }
            }
        },
        methods:{
            login:function(){
                //检测手机号是否正确
                console.log("login")
                //检测密码是否为空

                //网络请求
                //封装了promis
                util.post("user/login",this.user).then(res=>{
                    // console.log(res)
                    if(res.code!=0){
                        mui.toast(res.msg)
                        return
                    }
                    location.replace("//127.0.0.1/index.shtml")
                    mui.toast("登录成功,即将跳转")
                })
            },
        }
    })
</script>
{{end}}

相关推荐

  1. Go: IM系统基于xorm实现简单注册登录功能

    2024-07-19 08:30:02       20 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-19 08:30:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-19 08:30:02       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-19 08:30:02       58 阅读
  4. Python语言-面向对象

    2024-07-19 08:30:02       69 阅读

热门阅读

  1. C语言13 位域

    2024-07-19 08:30:02       22 阅读
  2. Linux:使用vim编辑文件为什么会影响目录的mtime

    2024-07-19 08:30:02       17 阅读
  3. 数据结构讲解

    2024-07-19 08:30:02       20 阅读
  4. C++:类的定义和实例化

    2024-07-19 08:30:02       21 阅读
  5. NumPy库学习之logspace函数

    2024-07-19 08:30:02       21 阅读
  6. springMVC前后端请求参数绑定和传递

    2024-07-19 08:30:02       17 阅读
  7. C++中的socket编程常用接口

    2024-07-19 08:30:02       19 阅读
  8. Redis实现打卡功能

    2024-07-19 08:30:02       20 阅读
  9. 探索.NET内存之海:垃圾回收的艺术与实践

    2024-07-19 08:30:02       22 阅读
  10. 【.NET全栈】ASP.NET开发Web应用——Web部件技术

    2024-07-19 08:30:02       18 阅读
  11. 基于Gunicorn、Flask和Docker的高并发部署

    2024-07-19 08:30:02       20 阅读