对于一些付费会员或者一些商业项目,为了保证单个用户的账号权益不会被滥用,并且提高系统的安全性,我们会限制单个账号在同一时间内只能有一台设备登录,
来给系统添加共享账号的检测能能力
这里是根据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()
}
}
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 项目优化方案之---实现单设备登录限制
发表评论 取消回复