对于一些付费会员或者一些商业项目,为了保证单个用户的账号权益不会被滥用,并且提高系统的安全性,我们会限制单个账号在同一时间内只能有一台设备登录,

来给系统添加共享账号的检测能能力

这里是根据jwt实现的,要实现单设备登录限制,这里最简单的方法也就是通过存入redis的方法,

生成jwt令牌之后,将令牌反馈前端的同时将其存入到redis中,而后在拦截器校验的时候,先校验jwt是否正确,再校验jwt是否和redis中的jwt一致,如果一致则说明是同一个用户。

上代码:

func LoginPwd(context *gin.Context) {
	var user entity.User
	db := common.GetDB()
	username := context.PostForm("username")
	password := context.PostForm("password")
	if len(username) < 5 || len(username) > 16 {
		response.Fail(context, "用户名长度在5-16之间", nil)
		return
	}
	if len(password) < 5 || len(password) > 16 {
		response.Fail(context, "密码长度在5-16之间", nil)
		return
	}
	if db.Where("name = ?", username).First(&user).RecordNotFound() {
		response.Fail(context, "用户名不存在", nil)
		return
	}
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
		response.Fail(context, "密码错误", nil)
		return
	}
	client := common.CreateClient()

	生成唯一id
	//uuid := generateId()
	//common.SetKeyValue(client, UserField, strconv.FormatInt(uuid, 20))

	//返回token
	token, err := common.ReleaseToken(user)
	if err != nil {
		response.Result(context, http.StatusInternalServerError, 500, "系统错误", nil)
		log.Println(err)
		return
	}

	err = common.SetKeyValueWithExpiration(client, JWT_USER_KEY, token, 1*time.Hour)
	if err != nil {
		response.Result(context, http.StatusInternalServerError, 500, "系统错误", nil)
		log.Println(err)
		return
	}

	// 在登录成功的响应中包含IP地址,或者根据需求进行记录、审计等操作
	log.Printf("User logged in with IP: %s", ip)

	response.Success(context, "登录成功", gin.H{
		"token": token,
	})

}

中间件校验代码:

		//连接redis
		client := common.CreateClient()
		//检查redis中是否有redis
		JwtValue, err := common.GetValue(client, JWT_USER_KEY)
		if err != nil {
			fmt.Println("获取jwt令牌失败", err)
			context.JSON(401, gin.H{"code": 401, "msg": "权限不足"})
			context.Abort()
			return
		}
		//比较jwt令牌,如果传来的令牌与redis中的令牌不同,则说明令牌已失效
		if JwtValue != tokenString {
			fmt.Println("jwt令牌已失效")
			context.JSON(401, gin.H{"code": 401, "msg": "权限不足"})
			context.Abort()
			return
		}

那么,如何实现在同一设备上登录不被顶掉呢?

比如同一个电脑不同的浏览器的话,登录的话就会有不同的jwt,此时如何让不同的浏览器能实现登录不被顶掉账号嘞(也就是单设备多客户端登录)

这里可以操作一下ip地址

在登录接口这里添加存储ip地址

// 获取客户端IP
	ip := context.ClientIP()

	// 在登录成功的响应中包含IP地址,或者根据需求进行记录、审计等操作
	log.Printf("User logged in with IP: %s", ip)

	err = common.SetKeyValue(client, UserIp, ip)

        然后在中间件检测这里,如果ip相同,且jwt可以识别的情况下,就允许其直接通过,这样就可以实现,在同一设备上,登录同一个账号不被顶下去

func AuthMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		tokenString := context.GetHeader("Authorization")
		if tokenString == "" { //|| !strings.HasPrefix(tokenString, "Bearer "
			context.JSON(401, gin.H{"code": 401, "msg": "权限不足"})
			context.Abort()
			return
		}
		//tokenString = tokenString[7:]
		token, claims, err := common.ParseToken(tokenString)
		log.Println("token:", token, "claims:", claims, "err:", err)
		if err != nil || !token.Valid { //解析失败或者token无效
			context.JSON(401, gin.H{"code": 401, "msg": "权限不足"})
			context.Abort()
			return
		}
		//连接redis
		client := common.CreateClient()
		//检查redis中是否有redis
		JwtValue, err := common.GetValue(client, JWT_USER_KEY)
		if err != nil {
			fmt.Println("获取jwt令牌失败", err)
			context.JSON(401, gin.H{"code": 401, "msg": "权限不足"})
			context.Abort()
			return
		}
		ip, _ := common.GetValue(client, UserIp)
		if ip == context.ClientIP() {
			//ip相等说明是同一个设备
			log.Println("ip相同,允许同设备放行")
			//获取用户信息
			userId := claims.UserId
			db := common.GetDB()
			var user entity.User
			db.First(&user, userId)
			if user.ID == 0 { //用户不存在
				context.JSON(401, gin.H{"code": 401, "msg": "权限不足"})
				context.Abort()
				return
			}
			//用户存在,将user信息写入下上文
			context.Set("user", user)
			context.Next()
		}
		//比较jwt令牌,如果传来的令牌与redis中的令牌不同,则说明令牌已失效
		if JwtValue != tokenString {
			fmt.Println("jwt令牌已失效")
			context.JSON(401, gin.H{"code": 401, "msg": "权限不足"})
			context.Abort()
			return
		}

		//获取用户信息
		userId := claims.UserId
		db := common.GetDB()
		var user entity.User
		db.First(&user, userId)
		if user.ID == 0 { //用户不存在
			context.JSON(401, gin.H{"code": 401, "msg": "权限不足"})
			context.Abort()
			return
		}
		//用户存在,将user信息写入下上文
		context.Set("user", user)

		context.Next()
	}
}

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部