(三)登录和注册(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)
}
  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
import socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('172.26.79.123', 9999)) server_socket.listen(5) clients = {} ##注册 def register(client_socket): # 处理客户端注册请求 # 获取注册信息 username = client_socket.recv(1024).decode() password = client_socket.recv(1024).decode() # 存储注册信息 # 这里可以使用文件、数据库等方式存储信息 # 为简单起见,我们在字典中存储注册信息 if username in clients: client_socket.send(b'Username already exists.') else: clients[username] = password client_socket.send(b'Register success.') ##登陆 def login(client_socket): # 处理客户端登录请求 # 获取登录信息 username = client_socket.recv(1024).decode() password = client_socket.recv(1024).decode() # 验证登录信息 if username in clients and clients[username] == password: client_socket.send(b'Login success.') else: client_socket.send(b'Login failed.') def handle_client(client_socket): # 处理客户端请求 while True: # 获取客户端请求类型 request_type = client_socket.recv(1024).decode() if request_type == 'register': register(client_socket) elif request_type == 'login': login(client_socket) else: client_socket.send(b'Invalid request type.') while True: client_socket, addr = server_socket.accept() clients[client_socket] = addr print('Connected with', addr) handle_client(client_socket)这是服务器端的代码,其中有注册和登陆的功能,请根据此代码写出对应的客户端的代码
05-26

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

席万里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值