@ -1,11 +1,18 @@
package model
package model
import (
import (
"go.uber.org/zap"
"encoding/pem"
// "goskeleton/app/global/consts"
"goskeleton/app/global/variable"
"goskeleton/app/global/variable"
"goskeleton/app/service/users/token_cache_redis"
"goskeleton/app/service/users/token_cache_redis"
"goskeleton/app/utils/md5_encrypt"
"goskeleton/app/utils/md5_encrypt"
rsa "goskeleton/app/utils/rsa"
"time"
"time"
"go.uber.org/zap"
"fmt"
// "github.com/google/uuid" // 导入uuid库
)
)
// 操作数据库喜欢使用gorm自带语法的开发者可以参考 GinSkeleton-Admin 系统相关代码
// 操作数据库喜欢使用gorm自带语法的开发者可以参考 GinSkeleton-Admin 系统相关代码
@ -23,11 +30,11 @@ type UsersModel struct {
BaseModel
BaseModel
UserName string ` gorm:"column:user_name" json:"user_name" `
UserName string ` gorm:"column:user_name" json:"user_name" `
Pass string ` json:"-" `
Pass string ` json:"-" `
Phone string ` json:"phone" `
// Phone string `json:"phone"`
RealName string ` gorm:"column:real_name" json:"real_name" `
// RealName string ` gorm:"column:real_name" json:"real_name"`
Status int ` json:"status" `
Status int ` json:"status" `
Token string ` json:"token" `
Token string ` json:"token" `
LastLoginIp string ` gorm:"column:last_login_ip" json:"last_login_ip" `
// LastLoginIp string ` gorm:"column:last_login_ip" json:"last_login_ip"`
}
}
// 表名
// 表名
@ -36,19 +43,21 @@ func (u *UsersModel) TableName() string {
}
}
// 用户注册(写一个最简单的使用账号、密码注册即可)
// 用户注册(写一个最简单的使用账号、密码注册即可)
func ( u * UsersModel ) Register ( userName , pass , userIp string ) bool {
func ( u * UsersModel ) Register ( userName , pass string ) bool {
sql := "INSERT INTO tb_users(user_name,pass,last_login_ip) SELECT ?,?,? FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM tb_users WHERE user_name=?)"
// userID:=uuid.New()
result := u . Exec ( sql , userName , pass , userIp , userName )
sql := "INSERT INTO tb_users(user_name,pass) SELECT ?,? FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM tb_users WHERE user_name=?)"
result := u . Exec ( sql , userName , pass , userName )
if result . RowsAffected > 0 {
if result . RowsAffected > 0 {
return true
return true
} else {
} else {
variable . ZapLog . Error ( "注册失败:" , zap . Error ( result . Error ) )
return false
return false
}
}
}
}
// 用户登录,
// 用户登录,
func ( u * UsersModel ) Login ( userName string , pass string ) * UsersModel {
func ( u * UsersModel ) Login ( userName string , pass string ) * UsersModel {
sql := "select id, user_name,real_name,pass,phone from tb_users where user_name=? limit 1"
sql := "select pass,id from tb_users where user_name=? limit 1"
result := u . Raw ( sql , userName ) . First ( u )
result := u . Raw ( sql , userName ) . First ( u )
if result . Error == nil {
if result . Error == nil {
// 账号密码验证成功
// 账号密码验证成功
@ -62,14 +71,14 @@ func (u *UsersModel) Login(userName string, pass string) *UsersModel {
}
}
//记录用户登陆( login) 生成的token, 每次登陆记录一次token
//记录用户登陆( login) 生成的token, 每次登陆记录一次token
func ( u * UsersModel ) OauthLoginToken ( userId int64 , token string , expiresAt int64 , clientIp string ) bool {
func ( u * UsersModel ) OauthLoginToken ( userId int64 , token string , expiresAt int64 ) bool {
sql := `
sql := `
INSERT INTO tb_oauth_access_tokens ( fr_user_id , action_name , token , expires_at , client_ip )
INSERT INTO tb_oauth_access_tokens ( fr_user_id , action_name , token , expires_at , client_ip )
SELECT ? , ' login ' , ? , ? , ? FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM tb_oauth_access_tokens a WHERE a . fr_user_id = ? AND a . action_name = ' login ' AND a . token = ? )
SELECT ? , ' login ' , ? , ? , ? FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM tb_oauth_access_tokens a WHERE a . fr_user_id = ? AND a . action_name = ' login ' AND a . token = ? )
`
`
//注意: token的精确度为秒, 如果在一秒之内, 一个账号多次调用接口生成的token其实是相同的, 这样写入数据库, 第二次的影响行数为0, 知己实际上操作仍然是有效的。
//注意: token的精确度为秒, 如果在一秒之内, 一个账号多次调用接口生成的token其实是相同的, 这样写入数据库, 第二次的影响行数为0, 知己实际上操作仍然是有效的。
//所以这里只判断无错误即可,判断影响行数的话,>=0 都是ok的
//所以这里只判断无错误即可,判断影响行数的话,>=0 都是ok的
if u . Exec ( sql , userId , token , time . Unix ( expiresAt , 0 ) . Format ( variable . DateFormat ) , clientIp, userId, token ) . Error == nil {
if u . Exec ( sql , userId , token , time . Unix ( expiresAt , 0 ) . Format ( variable . DateFormat ) , userId, token ) . Error == nil {
// 异步缓存用户有效的token到redis
// 异步缓存用户有效的token到redis
if variable . ConfigYml . GetInt ( "Token.IsCacheToRedis" ) == 1 {
if variable . ConfigYml . GetInt ( "Token.IsCacheToRedis" ) == 1 {
go u . ValidTokenCacheToRedis ( userId )
go u . ValidTokenCacheToRedis ( userId )
@ -91,27 +100,27 @@ func (u *UsersModel) OauthRefreshConditionCheck(userId int64, oldToken string) b
}
}
//用户刷新token
//用户刷新token
func ( u * UsersModel ) OauthRefreshToken ( userId , expiresAt int64 , oldToken , newToken , clientIp string ) bool {
func ( u * UsersModel ) OauthRefreshToken ( userId , expiresAt int64 , oldToken , newToken string ) bool {
sql := "UPDATE tb_oauth_access_tokens SET token=? ,expires_at=?, client_ip=?, updated_at=NOW(),action_name='refresh' WHERE fr_user_id=? AND token=?"
sql := "UPDATE tb_oauth_access_tokens SET token=? ,expires_at=?, updated_at=NOW(),action_name='refresh' WHERE fr_user_id=? AND token=?"
if u . Exec ( sql , newToken , time . Unix ( expiresAt , 0 ) . Format ( variable . DateFormat ) , clientIp , userId , oldToken ) . Error == nil {
if u . Exec ( sql , newToken , time . Unix ( expiresAt , 0 ) . Format ( variable . DateFormat ) , userId , oldToken ) . Error == nil {
// 异步缓存用户有效的token到redis
// 异步缓存用户有效的token到redis
if variable . ConfigYml . GetInt ( "Token.IsCacheToRedis" ) == 1 {
if variable . ConfigYml . GetInt ( "Token.IsCacheToRedis" ) == 1 {
go u . ValidTokenCacheToRedis ( userId )
go u . ValidTokenCacheToRedis ( userId )
}
}
go u . UpdateUserloginInfo ( clientIp , userId )
// go u.UpdateUserloginInfo(clientIp, userId)
return true
return true
}
}
return false
return false
}
}
// 更新用户登陆次数、最近一次登录ip、最近一次登录时间
// 更新用户登陆次数、最近一次登录ip、最近一次登录时间
func ( u * UsersModel ) UpdateUserloginInfo ( last_login_ip string , userId int64 ) {
// func (u *UsersModel) UpdateUserloginInfo(userId int64) {
sql := "UPDATE tb_users SET login_times=IFNULL(login_times,0)+1,last_login_ip=?,last_login_ time=? WHERE id=? "
// sql := "UPDATE tb_users SET login_times=IFNULL(login_times,0)+1,last_login_time=? WHERE id=? "
_ = u . Exec ( sql , last_login_ip , time . Now ( ) . Format ( variable . DateFormat ) , userId )
// _ = u.Exec(sql, time.Now().Format(variable.DateFormat), userId)
}
// }
//当用户更改密码后, 所有的token都失效, 必须重新登录
//当用户更改密码后, 所有的token都失效, 必须重新登录
func ( u * UsersModel ) OauthResetToken ( userId int , newPass , clientIp string ) bool {
func ( u * UsersModel ) OauthResetToken ( userId int , newPass string ) bool {
//如果用户新旧密码一致, 直接返回true, 不需要处理
//如果用户新旧密码一致, 直接返回true, 不需要处理
userItem , err := u . ShowOneItem ( userId )
userItem , err := u . ShowOneItem ( userId )
if userItem != nil && err == nil && userItem . Pass == newPass {
if userItem != nil && err == nil && userItem . Pass == newPass {
@ -123,8 +132,8 @@ func (u *UsersModel) OauthResetToken(userId int, newPass, clientIp string) bool
go u . DelTokenCacheFromRedis ( int64 ( userId ) )
go u . DelTokenCacheFromRedis ( int64 ( userId ) )
}
}
sql := "UPDATE tb_oauth_access_tokens SET revoked=1,updated_at=NOW(),action_name='ResetPass' ,client_ip=? WHERE fr_user_id=? "
sql := "UPDATE tb_oauth_access_tokens SET revoked=1,updated_at=NOW(),action_name='ResetPass' WHERE fr_user_id=? "
if u . Exec ( sql , clientIp, userId) . Error == nil {
if u . Exec ( sql , userId) . Error == nil {
return true
return true
}
}
}
}
@ -167,19 +176,19 @@ func (u *UsersModel) OauthCheckTokenIsOk(userId int64, token string) bool {
// 禁用一个用户的: 1.tb_users表的 status 设置为 0, tb_oauth_access_tokens 表的所有token删除
// 禁用一个用户的: 1.tb_users表的 status 设置为 0, tb_oauth_access_tokens 表的所有token删除
// 禁用一个用户的token请求( 本质上就是把tb_users表的 status 字段设置为 0 即可)
// 禁用一个用户的token请求( 本质上就是把tb_users表的 status 字段设置为 0 即可)
func ( u * UsersModel ) SetTokenInvalid ( userId int ) bool {
// func (u *UsersModel) SetTokenInvalid(userId int) bool {
sql := "delete from `tb_oauth_access_tokens` where `fr_user_id`=? "
// sql := "delete from `tb_oauth_access_tokens` where `fr_user_id`=? "
if u . Exec ( sql , userId ) . Error == nil {
// if u.Exec(sql, userId).Error == nil {
if u . Exec ( "update tb_users set status=0 where id=?" , userId ) . Error == nil {
// if u.Exec("update tb_users set status=0 where id=?", userId).Error == nil {
return true
// return true
}
// }
}
// }
return false
// return false
}
// }
//根据用户ID查询一条信息
//根据用户ID查询一条信息
func ( u * UsersModel ) ShowOneItem ( userId int ) ( * UsersModel , error ) {
func ( u * UsersModel ) ShowOneItem ( userId int ) ( * UsersModel , error ) {
sql := "SELECT `id`, `user_name`,`pass` , `real_name`, `phone`, `status` FROM `tb_users` WHERE `status`=1 and id=? LIMIT 1"
sql := "SELECT `id`, `user_name`,`pass` FROM `tb_users` WHERE `status`=1 and id=? LIMIT 1"
result := u . Raw ( sql , userId ) . First ( u )
result := u . Raw ( sql , userId ) . First ( u )
if result . Error == nil {
if result . Error == nil {
return u , nil
return u , nil
@ -189,32 +198,32 @@ func (u *UsersModel) ShowOneItem(userId int) (*UsersModel, error) {
}
}
// 查询数据之前统计条数
// 查询数据之前统计条数
func ( u * UsersModel ) counts ( userName string ) ( counts int64 ) {
// func (u *UsersModel) counts(userName string) (counts int64) {
sql := "SELECT count(*) as counts FROM tb_users WHERE status=1 and user_name like ?"
// sql := "SELECT count(*) as counts FROM tb_users WHERE status=1 and user_name like ?"
if res := u . Raw ( sql , "%" + userName + "%" ) . First ( & counts ) ; res . Error != nil {
// if res := u.Raw(sql, "%"+userName+"%").First(&counts); res.Error != nil {
variable . ZapLog . Error ( "UsersModel - counts 查询数据条数出错" , zap . Error ( res . Error ) )
// variable.ZapLog.Error("UsersModel - counts 查询数据条数出错", zap.Error(res.Error))
}
// }
return counts
// return counts
}
// }
// 查询(根据关键词模糊查询)
// 查询(根据关键词模糊查询)
func ( u * UsersModel ) Show ( userName string , limitStart , limitItems int ) ( counts int64 , temp [ ] UsersModel ) {
type baseInfo struct {
if counts = u . counts ( userName ) ; counts > 0 {
Id int64 ` gorm:"primaryKey" json:"id" `
sql := "SELECT `id`, `user_name`, `real_name`, `phone`,last_login_ip, `status`,created_at,updated_at FROM `tb_users` WHERE `status`=1 and user_name like ? LIMIT ?,?"
UserName string ` gorm:"column:user_name" json:"user_name" `
if res := u . Raw ( sql , "%" + userName + "%" , limitStart , limitItems ) . Find ( & temp ) ; res . RowsAffected > 0 {
return counts , temp
}
}
func ( u * UsersModel ) Info ( userName string ) ( temp baseInfo ) {
sql := "SELECT `id`, `user_name` FROM `tb_users` WHERE user_name= ?"
if res := u . Raw ( sql , userName ) . Scan ( & temp ) ; res . RowsAffected > 0 {
return temp
}
}
return 0 , nil
return
}
}
//新增
//新增
func ( u * UsersModel ) Store ( userName string , pass string , realName string , phone string , remark string ) bool {
func ( u * UsersModel ) Store ( userName string , pass string , realName string , phone string , remark string ) bool {
sql := "INSERT INTO tb_users(user_name,pass,real_name,phone,remark) SELECT ?,?,?,?,? FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM tb_users WHERE user_name=?)"
sql := "INSERT INTO tb_users(user_name,pass,real_name,phone,remark) SELECT ?,?,?,?,? FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM tb_users WHERE user_name=?)"
if u . Exec ( sql , userName , pass , realName , phone , remark , userName ) . RowsAffected > 0 {
return u . Exec ( sql , userName , pass , realName , phone , remark , userName ) . RowsAffected > 0
return true
// return false
}
return false
}
}
//UpdateDataCheckUserNameIsUsed 更新前检查新的用户名是否已经存在(避免和别的账号重名)
//UpdateDataCheckUserNameIsUsed 更新前检查新的用户名是否已经存在(避免和别的账号重名)
@ -225,31 +234,175 @@ func (u *UsersModel) UpdateDataCheckUserNameIsUsed(userId int, userName string)
}
}
//更新
//更新
func ( u * UsersModel ) Update ( id int , userName string , pass string , realName string , phone string , remark string , clientIp string ) bool {
func ( u * UsersModel ) NameUpdate ( id int , userName string ) bool {
sql := "update tb_users set user_name=?,pass=?,real_name=?,phone=?,remark=? WHERE status=1 AND id=?"
sql := "update tb_users set user_name=? WHERE status=1 AND id=?"
if u . Exec ( sql , userName , pass , realName , phone , remark , id ) . RowsAffected >= 0 {
return u . Exec ( sql , userName , id ) . RowsAffected >= 0
if u . OauthResetToken ( id , pass , clientIp ) {
return true
}
}
func ( u * UsersModel ) UpdatePassword ( id int , userName , oldpass , newpass string ) * UsersModel {
sql := "select pass from tb_users where id=? limit 1"
result := u . Raw ( sql , id ) . First ( u )
if result . Error == nil {
// 账号密码验证成功
if len ( u . Pass ) > 0 && ( u . Pass == md5_encrypt . Base64Md5 ( oldpass ) ) {
if u . OauthResetToken ( id , md5_encrypt . Base64Md5 ( newpass ) ) {
variable . ZapLog . Info ( "密码更改后, token重已置" )
} else {
variable . ZapLog . Error ( "密码更改后, token重置失败" )
}
}
return false
// 更新密码
sql = "update tb_users set pass=? WHERE status=1 AND id=?"
if u . Exec ( sql , newpass , id ) . RowsAffected >= 0 {
return u
}
}
} else {
variable . ZapLog . Error ( "根据账号查询单条记录出错:" , zap . Error ( result . Error ) )
}
return nil
}
}
//删除用户以及关联的token记录
//删除用户以及关联的token记录
func ( u * UsersModel ) Destroy ( id int ) bool {
func ( u * UsersModel ) Destroy ( id int ,userName , pass string ) bool {
// 删除用户时, 清除用户缓存在redis的全部token
// 删除用户时, 清除用户缓存在redis的全部token
if variable . ConfigYml . GetInt ( "Token.IsCacheToRedis" ) == 1 {
if variable . ConfigYml . GetInt ( "Token.IsCacheToRedis" ) == 1 {
go u . DelTokenCacheFromRedis ( int64 ( id ) )
go u . DelTokenCacheFromRedis ( int64 ( id ) )
}
}
// 检查密码是否正确
sql := "select pass from tb_users where id=? limit 1"
result := u . Raw ( sql , id ) . First ( u )
if result . Error == nil {
// 账号密码验证成功
if len ( u . Pass ) > 0 && ( u . Pass == md5_encrypt . Base64Md5 ( pass ) ) {
// 删除用户密钥
u . deleteKeyFromDB ( userName )
// 删除用户
if u . Delete ( u , id ) . Error == nil {
if u . Delete ( u , id ) . Error == nil {
// 删除token
if u . OauthDestroyToken ( id ) {
if u . OauthDestroyToken ( id ) {
return true
return true
}
}
}
}
}
}
return false
return false
}
}
func ( u * UsersModel ) Logout ( id int , userName string ) bool {
// 登出用户时, 清除用户缓存在redis的全部token
if variable . ConfigYml . GetInt ( "Token.IsCacheToRedis" ) == 1 {
go u . DelTokenCacheFromRedis ( int64 ( id ) )
}
// 删除token
if ! u . OauthDestroyToken ( id ) {
// return true
variable . ZapLog . Error ( "登出时删除token失败" )
}
// 删除用户密钥
u . deleteKeyFromDB ( userName )
return true
}
// 返回用户密码加密所需的公钥
func ( u * UsersModel ) PublicKey ( userName string ) [ ] byte {
// sql :="SELECT public_key FROM `tb_rsa_keypair` WHERE user_name = ?"
// var publicKey string
// if res := u.Raw(sql,userName).Scan(&publicKey); res.RowsAffected > 0 {
// return []byte(publicKey)
// }
// // 不存在密钥,重新生成密钥
// return u.GenerateKeyPair(userName)
publicKey , err := u . getKeyFromDB ( userName , "public_key" )
if err == nil {
return [ ] byte ( publicKey )
}
// 不存在密钥,重新生成密钥
pubPEM , priPEM , err := rsa . GenerateRSAKeyPair ( )
if err != nil {
return nil
}
if err := u . storeKeyPair ( userName , pubPEM , priPEM ) ; err != nil {
return nil
}
return pubPEM
}
// 返回服务器解密所需的私钥
func ( u * UsersModel ) PrivateKey ( userName string ) [ ] byte {
// sql :="SELECT private_key FROM `tb_rsa_keypair` WHERE user_name = ?"
// var privateKey string
// if res := u.Raw(sql,userName).Scan(&privateKey); res.RowsAffected > 0 {
// priPEM:=pem.EncodeToMemory(&pem.Block{
// Type: "PRIVATE KEY",
// Bytes: []byte(privateKey),
// })
// return priPEM
// }
// return nil
privateKey , err := u . getKeyFromDB ( userName , "private_key" )
if err == nil {
priPEM := pem . EncodeToMemory ( & pem . Block {
Type : "PRIVATE KEY" ,
Bytes : [ ] byte ( privateKey ) ,
} )
return priPEM
}
return nil
}
// func (u *UsersModel)GenerateKeyPair(userName string)[]byte{
// priKey,err:=rsa.GenerateKey(rand.Reader,variable.ConfigYml.GetInt("RSA.keySize"))
// if err!=nil{
// return nil
// }
// pubKey:=&priKey.PublicKey
// // convert to byte slice
// priASN1:=x509.MarshalPKCS1PrivateKey(priKey)
// priPEM:=pem.EncodeToMemory(&pem.Block{
// Type: "PRIVATE KEY",
// Bytes: priASN1,
// })
// pubASN1,err:=x509.MarshalPKIXPublicKey(pubKey)
// if err!=nil{
// return nil
// }
// pubPEM := pem.EncodeToMemory(&pem.Block{
// Type: "PUBLIC KEY",
// Bytes: pubASN1,
// })
// // store key_pair
// sql:="INSERT INTO tb_rsa_keypair(user_name,public_key,private_key) VALUES (?,?,?)"
// if u.Exec(sql, userName, pubPEM, priPEM).RowsAffected > 0 {
// return pubPEM
// }
// return nil
// }
// 从数据库获取密钥(公钥/私钥)
func ( u * UsersModel ) getKeyFromDB ( userName , keyType string ) ( string , error ) {
sql := fmt . Sprintf ( "SELECT %s FROM `tb_rsa_keypair` WHERE user_name = ?" , keyType )
var key string
if res := u . Raw ( sql , userName ) . Scan ( & key ) ; res . RowsAffected > 0 {
return key , nil
}
return "" , fmt . Errorf ( "key not found" )
}
// 从数据库删除用户的所有密钥
func ( u * UsersModel ) deleteKeyFromDB ( userName string ) error {
sql := "DELETE FROM `tb_rsa_keypair` WHERE user_name = ?"
if u . Exec ( sql , userName ) . RowsAffected > 0 {
return nil
}
return fmt . Errorf ( "failed to delete key pair" )
}
// 存储公私钥对到数据库
func ( u * UsersModel ) storeKeyPair ( userName string , pubPEM , priPEM [ ] byte ) error {
sql := "INSERT INTO tb_rsa_keypair(user_name, public_key, private_key) VALUES (?, ?, ?)"
if u . Exec ( sql , userName , pubPEM , priPEM ) . RowsAffected > 0 {
return nil
}
return fmt . Errorf ( "failed to store key pair" )
}
// 后续两个函数专门处理用户 token 缓存到 redis 逻辑
// 后续两个函数专门处理用户 token 缓存到 redis 逻辑
func ( u * UsersModel ) ValidTokenCacheToRedis ( userId int64 ) {
func ( u * UsersModel ) ValidTokenCacheToRedis ( userId int64 ) {