(三)登录和注册(handle_auto.go)

登录和注册(handle_auto.go)

在这里插入图片描述

文章目录

  • 登录和注册(handle_auto.go)
    • 一、所需要的结构体信息
    • 二、注册
    • 三、登录
    • 四、退出

一、所需要的结构体信息

type UserAuth struct{}

type LoginReq struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required"`
}

type RegisterReq struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required,min=4,max=20"`
	Code     string `json:"code" binding:"required"`
}

type LoginVO struct {
	model.UserInfo
	// 点赞 Set: 用于记录用户点赞过的文章, 评论
	ArticleLikeSet []string `json:"article_like_set"`
	CommentLikeSet []string `json:"comment_like_set"`
	Token          string   `json:"token"`
}

二、注册

因为原本的作者没有写注册的功能,我就自己写了一个简单的注册功能。

1.注册的路由

base.POST("/register", userAuthAPI.Register)    // 注册

2.验证码的功能函数

handler层的函数

  • 使用qq邮箱发送验证
  • 限制发送时间
  • 使用redis存储验证码,时间1分钟
func (*UserAuth) SendCode(c *gin.Context) {
	// 发送验证码限制于1分钟发一次
	elapsedTime := time.Since(lastEmailSent)
	remaingTime := time.Minute - elapsedTime
	if elapsedTime < time.Minute {
		c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("发送验证码过于频繁,请稍后再试,剩余时间:%d秒", int(remaingTime.Seconds()))})
		return
	}
	// 从请求参数中获取邮箱地址
	email := c.Query("email")
	if email == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "未提供邮箱地址"})
		return
	}
	// 发送邮件验证码
	err := SendEmail(email, c)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "发送邮件失败"})
		return
	}
	// 发送邮件成功,返回成功响应给前端
	lastEmailSent = time.Now()
	ReturnSuccess(c, "验证码发送成功")
}

// 发送邮件到指定邮箱
func SendEmail(email string, c *gin.Context) error {
	// 邮箱配置信息
	config := gomail.NewDialer("smtp.qq.com", 465, "xxx@qq.com", "自己的授权码")
	//config.TLSConfig.ServerName = "smtp.qq.com"
	//config.TLSConfig.InsecureSkipVerify = false
	// 创建邮件内容
	message := gomail.NewMessage()
	message.SetHeader("From", "xxx@qq.com")
	message.SetHeader("To", email)
	message.SetHeader("Subject", "验证码")
	// 获取随机验证码
	computing := utils2.GetRandNums(6)
	message.SetBody("text/html", fmt.Sprintf("欢迎使用席万里的博客系统,这是您的注册验证码:%s", computing))
	// 连接并发送邮件
	dialer, err := config.Dial()
	if err != nil {
		return err
	}
	defer dialer.Close()
	if err = gomail.Send(dialer, message); err != nil {
		return err
	}
	// 将验证码存到redis中,确保输入的是正确的
	rdb := GetRDB(c)
	// 以email为key,mima为val存入redis,只存在1分钟
	rdb.Set(rctx, email, computing, time.Minute)
	return nil
}

3.注册的主功能函数

  • 用到了密码加密
  • 随机验证码的生成
  • 数据的写入
func (*UserAuth) Register(c *gin.Context) {
	var req RegisterReq
	if err := c.ShouldBindJSON(&req); err != nil {
		ReturnError(c, g2.ErrRequest, err)
		return
	}
	db := GetDB(c)
	rdb := GetRDB(c)
	// 检查邮箱是否已经被注册
	_, err := model.GetUserAuthInfoByName(db, req.Username)
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			ReturnError(c, g2.ErrDbOp, err)
			return
		}
	} else {
		ReturnError(c, g2.ErrMailAleradyUsed, "邮箱已经被使用")
		return
	}
	// 取出来验证码码做判断 看是否正确
	computing, err := rdb.Get(rctx, req.Username).Result()
	if err != nil {
		// 处理获取验证码失败的情况
		ReturnError(c, g2.FailResult, err)
		return
	}
	// 如果验证码不正确,则返回错误信息
	if computing != req.Code {
		ReturnError(c, g2.ErrVerificationCode, "验证码错误")
		return
	}
	// 将密码加密
	hashpassword, _ := utils2.BcryptHash(req.Password)
	err = model.RegisterDB(db, req.Username, hashpassword)
	if err != nil {
		ReturnError(c, g2.ErrDbOp, "注册失败")
	}
	ReturnSuccess(c, "注册成功")
}

4.数据库层的邮箱查重函数

func GetUserAuthInfoByName(db *gorm.DB, name string) (*UserAuth, error) {
	var userAuth UserAuth
	result := db.Where(&UserAuth{Username: name}).First(&userAuth)
	return &userAuth, result.Error
}

5.数据查入、生成随机验证码、mimajiammi

// 数据查入
// 用户注册 用户名,密码
func RegisterDB(db *gorm.DB, username, hashpassword string) error {
	// 创建新用户认证信息
	newUserAuth := &UserAuth{
		Model: Model{
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
		},
		Username: username,
		Password: hashpassword,
	}
	// 保存新用户认证信息到数据库
	if err := db.Create(newUserAuth).Error; err != nil {
		return err
	}
	return nil
}
// 生成随机验证码
func GetRandNums(digits int) string {
	rand.Seed(time.Now().UnixNano())
	numeric := [10]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	var sb strings.Builder
	for i := 0; i < digits; i++ {
		fmt.Fprintf(&sb, "%d", numeric[rand.Intn(len(numeric))])
	}
	return sb.String()
}

// 密码加密
func BcryptHash(str string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(str), bcrypt.DefaultCost)
	return string(bytes), err
}

三、登录

登录功能也用到了查重,校验密码hash等功能,这里不在讲述。

  • 生成jwt token
  • 使用MD5加密
  • 使用Session存储认证ID
  • 使用Redis存储在线信息
func (*UserAuth) Login(c *gin.Context) {
	var req LoginReq
	if err := c.ShouldBindJSON(&req); err != nil {
		ReturnError(c, g2.ErrRequest, err)
		return
	}
	db := GetDB(c)
	rdb := GetRDB(c)
	userAuth, err := model.GetUserAuthInfoByName(db, req.Username)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			ReturnError(c, g2.ErrUserNotExist, nil)
			return
		}
		ReturnError(c, g2.ErrDbOp, err)
		return
	}
	// 检查密码是否正确
	if !utils2.BcryptCheck(req.Password, userAuth.Password) {
		ReturnError(c, g2.ErrPassword, nil)
		return
	
	// 获取 IP 相关信息 FIXME: 好像无法读取到 ip 信息
	ipAddress := utils2.IP.GetIpAddress(c)
	ipSource := utils2.IP.GetIpSourceSimpleIdle(ipAddress)

	userInfo, err := model.GetUserInfoById(db, userAuth.UserInfoId)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			ReturnError(c, g2.ErrUserNotExist, nil)
			return
		}
		ReturnError(c, g2.ErrDbOp, err)
		return
	}
	roleIds, err := model.GetRoleIdsByUserId(db, userAuth.ID)
	if err != nil {
		ReturnError(c, g2.ErrDbOp, err)
		return
	}
	articleLikeSet, err := rdb.SMembers(rctx, g2.ARTICLE_USER_LIKE_SET+strconv.Itoa(userAuth.ID)).Result()
	if err != nil {
		ReturnError(c, g2.ErrDbOp, err)
		return
	}
	commentLikeSet, err := rdb.SMembers(rctx, g2.COMMENT_USER_LIKE_SET+strconv.Itoa(userAuth.ID)).Result()
	if err != nil {
		ReturnError(c, g2.ErrDbOp, err)
		return
	}
	// 登录信息正确, 生成 Token
	// UUID 生成方法: ip + 浏览器信息 + 操作系统信息
	// uuid := utils.MD5(ipAddress + browser + os)
	conf := g.Conf.JWT
	token, err := jwt.GenToken(conf.Secret, conf.Issuer, int(conf.Expire), userAuth.ID, roleIds)
	if err != nil {
		ReturnError(c, g2.ErrTokenCreate, err)
		return
	}
	// 更新用户验证信息: ip 信息 + 上次登录时间
	err = model.UpdateUserLoginInfo(db, userAuth.ID, ipAddress, ipSource)
	if err != nil {
		ReturnError(c, g2.ErrDbOp, err)
		return
	}
	slog.Info("用户登录成功: " + userAuth.Username)
	session := sessions.Default(c)
	session.Set(g2.CTX_USER_AUTH, userAuth.ID)
	session.Save()
	// 删除 Redis 中的离线状态
	offlineKey := g2.OFFLINE_USER + strconv.Itoa(userAuth.ID)
	rdb.Del(rctx, offlineKey).Result()
	ReturnSuccess(c, LoginVO{
		UserInfo: *userInfo,
		ArticleLikeSet: articleLikeSet,
		CommentLikeSet: commentLikeSet,
		Token:          token,
	})
}

1.use Redis 缓存用户收藏和喜欢的文章

	articleLikeSet, err := rdb.SMembers(rctx, g2.ARTICLE_USER_LIKE_SET+strconv.Itoa(userAuth.ID)).Result()
	if err != nil {
		ReturnError(c, g2.ErrDbOp, err)
		return
	}
	commentLikeSet, err := rdb.SMembers(rctx, g2.COMMENT_USER_LIKE_SET+strconv.Itoa(userAuth.ID)).Result()
	if err != nil {
		ReturnError(c, g2.ErrDbOp, err)
		return
	}

四、退出

func (*UserAuth) Logout(c *gin.Context) {
	c.Set(g2.CTX_USER_AUTH, nil)

	// 已经退出登录
	auth, _ := CurrentUserAuth(c)
	if auth == nil {
		ReturnSuccess(c, nil)
		return
	}

	session := sessions.Default(c)
	session.Delete(g2.CTX_USER_AUTH)
	session.Save()

	// 删除 Redis 中的在线状态
	rdb := GetRDB(c)
	onlineKey := g2.ONLINE_USER + strconv.Itoa(auth.ID)
	rdb.Del(rctx, onlineKey)

	ReturnSuccess(c, nil)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/576191.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【汇编语言】直接定址表

【汇编语言】直接定址表 文章目录 【汇编语言】直接定址表前言一、移位指令移位指令过程逻辑移位指令shl 和 shr 二、操作显存数据显示的原理显示缓冲区的结构显示信息的一种“直接”方式 三、描述内存单元的标号关于标号去了冒号的数据标号数据标号同时描述内存地址和单元长度…

前端JS必用工具【js-tool-big-box】,防抖和节流的方法调用学习

这一小节&#xff0c;我们针对前端工具包&#xff08;npm&#xff09;js-tool-big-box的使用做一些讲解&#xff0c;主要是防抖和节流方面的。 目录 前言 1 安装和引入 2 防抖的调用学习 3 节流的调用学习 4 使用方法总结 前言 在前端项目中&#xff0c;经常涉及到防抖…

CNAS软件测评报告收费标准

随着信息技术的快速发展&#xff0c;软件测评在保障软件质量、提升用户体验等方面扮演着越来越重要的角色。CNAS&#xff08;中国合格评定国家认可委员会&#xff09;作为国内权威的认可机构&#xff0c;其软件测评报告收费标准受到了广泛关注。本文旨在解析CNAS软件测评报告的…

[华为OD]幼儿园两个班的小朋友 100

题目&#xff1a; 幼儿园两个班的小朋友在排队时混在了一起&#xff0c;每位小朋友都知道自己是否与前面一位小朋友 是否同班&#xff0c;请你帮忙把同班的小朋友找出来。 小朋友的编号为整数&#xff0c;与前一位小朋友同班用 Y 表示&#xff0c;不同班用 N 表示。 输入描…

索引【MySQL】

文章目录 什么是索引测试表 磁盘和 MySQL 的交互了解磁盘MySQL 的工作原理Buffer Pool 理解索引引入Page 的结构页内目录&#xff08;Page Directory&#xff09;多页情况B 树和 B树聚簇索引和非聚簇索引 主键索引创建 唯一索引主要特点与主键索引的区别使用场景创建 联合索引工…

思维+数学期望,CF 1525E Assimilation IV

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1525E - Codeforces 二、解题报告 1、思路分析 看数据量盲猜O(…

树的层序遍历(详解)

下面以一道力扣题为例&#xff1a; 代码和解释如下&#xff1a; /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(…

零基础HTML教程(31)--HTML5多媒体

文章目录 1. 背景2. audio音频3. video视频4. audio与video常用属性5. 小结 1. 背景 在H5之前&#xff0c;我们要在网页上播放音频、视频&#xff0c;需要借助第三方插件。 这些插件里面最火的就是Flash了&#xff0c;使用它有几个问题&#xff1a; 首先要单独安装Flash&…

华为Pura 70系列,一种关于世界之美的可能

1874年&#xff0c;莫奈创作了《印象日出》的油画&#xff0c;在艺术界掀起了一场革命。当时的主流艺术&#xff0c;是追求细节写实&#xff0c;追求场面宏大的学院派。他们称莫奈等人是“印象派”&#xff0c;认为莫奈的画追求光影表达&#xff0c;追求描绘抽象的意境&#xf…

echarts地图叠加百度地图底板实现数据可视化

这里写自定义目录标题 echarts地图叠加百度地图实现数据可视化echarts地图叠加百度地图实现数据可视化 实现数据可视化时,个别情况下需要在地图上实现数据的可视化,echarts加载geojson数据可以实现以地图形式展示数据,例如分层设色或者鼠标hover展示指标值,但如果要将echa…

【Redis 开发】一人一单,超卖问题(悲观锁,乐观锁,分布式锁)

锁 悲观锁乐观锁第一种&#xff1a;版本号法第二种&#xff1a;CAS法实现乐观锁 悲观锁与乐观锁的比较 一人一单分布式锁Redis实现分布式锁 悲观锁 认为线程问题一定会发生&#xff0c;因此在操作数据库之前先获取锁&#xff0c;确保线程串行执行&#xff0c;例如Synchronized…

好的猫咪主食冻干到底该咋选?品控稳定的主食冻干推荐

315中国之声报道的河北省邢台市南和区某宠粮代工厂的“行业潜规则”&#xff0c;给各位铲屎官拉响了警钟。配料表上写的鸡肉含量为52%&#xff0c;新鲜鸡小胸含量为20%&#xff0c;所谓的鲜鸡肉其实就是鸡肉粉。本来养宠物是为了让自己身心愉悦&#xff0c;但这样的行业乱象弄得…

prompt提示词:AI英语词典优化版Pro,让AI教你学英语,通过AI实现一个网易有道英语词典

目录 一、前言二、效果对比三、优化《AI英语词典》提示词四、其他获奖作品链接 一、前言 不可思议&#xff01;我的AI有道英语字典助手竟然与百度千帆AI应用创意挑战赛K12教育主题赛榜首作品差之毫厘 &#xff0c;真的是高手都是惺惺相惜的&#xff0c;哈哈&#xff0c;自恋一…

发票管理设计方案

1、背景介绍 在供应链金融业务场景下&#xff0c;供应商可以依赖与大型企业的合同、发票信息&#xff0c;到金融机构进行融资。本文探讨发票管理的设计方案。 2、需求分析 如上图所示&#xff0c;发票管理主要分为发票信息的管理以及发票可用余额管理2个部分。 名词解释&…

Docker-概念及配置(超详细)

docker 第一章 1、什么是docker 答&#xff1a;docker是一种容器引擎&#xff0c;通过docker可以将软件安装并且配置好以后&#xff0c;做成一个镜像文件。通过这个镜像文件可以快速的安装、配置软件环境 2、3个概念 【docker镜像】&#xff1a;将软件环境安装配置好以后产生…

【QA】Git的底层原理

前言 本文通过一个简单的示例&#xff0c;来理解Git的底层原理。 示例 1、新建本地仓库并上传第一个文件 相关步骤&#xff1a; 新建仓库及创建文件查看文件状态将文件添加到暂存区将文件提交到本地仓库 HMTeenLAPTOP-46U4TV6K MINGW64 /d/GSF_Data/Github/Java/Git/git-…

一张图带你理解 绝对路径 和 相对路径

绝对路径和相对路径是用于定位文件或目录位置的两种不同方式。 1、绝对路径&#xff1a; 绝对路径是从文件系统的根目录开始的完整路径&#xff0c;可以唯一地标识文件或目录的位置。 绝对路径是以根目录开始的 在Unix/Linux系统中&#xff0c;绝对路径是类似于/home/user/do…

2024 OceanBase 开发者大会:OceanBase 4.3正式发布,打造近PB级实时分析数据库

4月20日&#xff0c;2024 OceanBase开发者大会盛大召开&#xff0c;吸引了50余位业界知名的数据库专家和爱好者&#xff0c;以及来自全国各地的近600名开发者齐聚一堂。他们围绕一体化、多模、TP与AP融合等前沿技术趋势展开深入讨论&#xff0c;分享场景探索的经验和最佳实践&a…

基于DEAP数据集的四种机器学习方法的情绪分类

在机器学习领域&#xff0c;KNN&#xff08;K-Nearest Neighbors&#xff09;、SVM&#xff08;Support Vector Machine&#xff09;、决策树&#xff08;Decision Tree&#xff09;和随机森林&#xff08;Random Forest&#xff09;是常见且广泛应用的算法。 介绍 1. KNN&am…

Let‘s Move Sui:解锁区块链高性能潜力,探索创新开发体验

Sui 是基于第一原理重新设计和构建而成的 L1 公链&#xff0c;旨在为创作者和开发者提供能够承载 Web3 中下一个十亿用户的开发平台。 今年&#xff0c;Sui 的原生编程语言 Move 迎来了重要的更新升级。2024 版将增加枚举 Enums、宏函数、Method 语法等功能。这些重要的新功能为…