update WeChat Login
This commit is contained in:
parent
818d1f0019
commit
825a3c88ce
@ -2,6 +2,7 @@ package web
|
||||
|
||||
import (
|
||||
"catface/app/global/consts"
|
||||
"catface/app/global/errcode"
|
||||
"catface/app/global/variable"
|
||||
"catface/app/model"
|
||||
"catface/app/service/users/curd"
|
||||
@ -158,27 +159,31 @@ func (u *Users) WeixinLogin(context *gin.Context) {
|
||||
weixinRes, err := weixin.Code2Session(code)
|
||||
if err != nil {
|
||||
// 解析微信登录成功,返回用户信息
|
||||
response.Fail(context, consts.CurdLoginFailCode, consts.CurdLoginFailMsg, err)
|
||||
response.Fail(context, errcode.ErrWeixinApi, errcode.ErrMsg[errcode.ErrWeixinApi], err)
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 执行 CURD
|
||||
user, err := model.CreateUserFactory("mysql").WeixinLogin(weixinRes.OpenId, weixinRes.SessionKey, userName, userAvatar, userIp)
|
||||
if err == nil && user != nil {
|
||||
if user.Id > 0 {
|
||||
userModel, err := model.CreateUserFactory("").WeixinLogin(weixinRes.OpenId, weixinRes.SessionKey, userName, userAvatar, userIp)
|
||||
if err == nil && userModel != nil {
|
||||
if userModel.Id > 0 {
|
||||
// 3. 生成 token
|
||||
token, err := userstoken.CreateUserFactory().GenerateToken(user.Id, userName, "", 0)
|
||||
if err != nil {
|
||||
response.Fail(context, consts.CurdLoginFailCode, consts.CurdLoginFailMsg, err)
|
||||
return
|
||||
userTokenFactory := userstoken.CreateUserFactory()
|
||||
if userToken, err := userTokenFactory.GenerateToken(userModel.Id, userModel.UserName, userModel.SessionKey, variable.ConfigYml.GetInt64("Token.JwtTokenCreatedExpireAt")); err == nil {
|
||||
if userTokenFactory.RecordLoginToken(userToken, context.ClientIP()) {
|
||||
data := gin.H{
|
||||
"userId": userModel.Id,
|
||||
"user_name": userName,
|
||||
"permission": userModel.Permission,
|
||||
"token": userToken,
|
||||
"updated_at": time.Now().Format(variable.DateFormat),
|
||||
}
|
||||
response.Success(context, consts.CurdStatusOkMsg, data)
|
||||
go model.CreateUserFactory("").UpdateUserloginInfo(context.ClientIP(), userModel.Id) // TODO 暂时的解决方案就是直接重新一个实例
|
||||
return
|
||||
}
|
||||
}
|
||||
// 4. 返回 token
|
||||
res := gin.H{
|
||||
"userId": user.Id,
|
||||
"permission": user.Permission,
|
||||
"token": token,
|
||||
}
|
||||
response.Success(context, consts.CurdStatusOkMsg, res)
|
||||
// TODO 这里不写错误处理?
|
||||
}
|
||||
} else {
|
||||
response.Fail(context, consts.CurdLoginFailCode, consts.CurdLoginFailMsg, "")
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"catface/app/global/variable"
|
||||
"catface/app/service/users/token_cache_redis"
|
||||
"catface/app/utils/md5_encrypt"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@ -30,6 +31,7 @@ type UsersModel struct {
|
||||
// TAG 状态管理
|
||||
Status uint8 `json:"status"` // QUESTION
|
||||
Token string `json:"token"`
|
||||
LoginTimes uint64 `json:"login_times"`
|
||||
LastLoginIp string `gorm:"column:last_login_ip" json:"last_login_ip"`
|
||||
// TAG MySELF
|
||||
UserAvatar string `gorm:"column:user_avatar;size:255" json:"user_avatar"` // TODO 暂时存储 url,之后考虑需要把文件上传到 Nginx
|
||||
@ -41,7 +43,7 @@ type UsersModel struct {
|
||||
|
||||
// 表名
|
||||
func (u *UsersModel) TableName() string { // TIP GORM 也会自动调用这个函数。
|
||||
return "users"
|
||||
return "tb_users"
|
||||
}
|
||||
|
||||
// 用户注册(写一个最简单的使用账号、密码注册即可)
|
||||
@ -73,17 +75,24 @@ func (u *UsersModel) Login(userName string, pass string) *UsersModel {
|
||||
// 记录用户登陆(login)生成的token,每次登陆记录一次token
|
||||
func (u *UsersModel) OauthLoginToken(userId int64, token string, expiresAt int64, clientIp string) bool {
|
||||
sql := `
|
||||
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=? )
|
||||
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=?
|
||||
)
|
||||
`
|
||||
//注意:token的精确度为秒,如果在一秒之内,一个账号多次调用接口生成的token其实是相同的,这样写入数据库,第二次的影响行数为0,知己实际上操作仍然是有效的。
|
||||
//所以这里只判断无错误即可,判断影响行数的话,>=0 都是ok的
|
||||
if u.Exec(sql, userId, token, time.Unix(expiresAt, 0).Format(variable.DateFormat), clientIp, userId, token).Error == nil {
|
||||
if err := u.Exec(sql, userId, token, time.Unix(expiresAt, 0).Format(variable.DateFormat), clientIp, userId, token).Error; err == nil {
|
||||
// 异步缓存用户有效的token到redis
|
||||
if variable.ConfigYml.GetInt("Token.IsCacheToRedis") == 1 {
|
||||
go u.ValidTokenCacheToRedis(userId)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -115,8 +124,10 @@ func (u *UsersModel) OauthRefreshToken(userId, expiresAt int64, oldToken, newTok
|
||||
|
||||
// 更新用户登陆次数、最近一次登录ip、最近一次登录时间
|
||||
func (u *UsersModel) UpdateUserloginInfo(last_login_ip string, userId int64) {
|
||||
sql := "UPDATE tb_users SET login_times=IFNULL(login_times,0)+1,last_login_ip=?,last_login_time=? WHERE id=? "
|
||||
_ = u.Exec(sql, last_login_ip, time.Now().Format(variable.DateFormat), userId)
|
||||
// sql := "UPDATE tb_users SET login_times=IFNULL(login_times,0)+1,last_login_ip=?,last_login_time=? WHERE id=? "
|
||||
// _ = u.Exec(sql, last_login_ip, time.Now().Format(variable.DateFormat), userId)
|
||||
sql := "UPDATE tb_users SET login_times=IFNULL(login_times,0)+1,last_login_ip=? WHERE id=? "
|
||||
_ = u.Exec(sql, last_login_ip, userId)
|
||||
}
|
||||
|
||||
// 当用户更改密码后,所有的token都失效,必须重新登录
|
||||
@ -260,7 +271,6 @@ func (u *UsersModel) Destroy(id int) bool {
|
||||
}
|
||||
|
||||
// 后续两个函数专门处理用户 token 缓存到 redis 逻辑
|
||||
|
||||
func (u *UsersModel) ValidTokenCacheToRedis(userId int64) {
|
||||
tokenCacheRedisFact := token_cache_redis.CreateUsersTokenCacheFactory(userId)
|
||||
if tokenCacheRedisFact == nil {
|
||||
@ -313,21 +323,28 @@ func (u *UsersModel) DelTokenCacheFromRedis(userId int64) {
|
||||
* @description
|
||||
* @return {*}
|
||||
*/
|
||||
func (u *UsersModel) WeixinLogin(openId, sessionKey, name, avatar, ip string) (temp *UsersModel, err error) {
|
||||
func (u *UsersModel) WeixinLogin(openId, sessionKey, name, avatar, ip string) (temp *UsersModel, errTemp error) {
|
||||
db := u.DB
|
||||
|
||||
var user UsersModel
|
||||
if result := db.Where("open_id = ?", openId).First(&user); result.Error != nil {
|
||||
result := db.Where("open_id = ?", openId).First(&user)
|
||||
|
||||
if result.Error == nil && result.RowsAffected > 0 {
|
||||
temp = &user
|
||||
} else if result.Error == gorm.ErrRecordNotFound {
|
||||
newUser := UsersModel{OpenId: openId, UserName: name, UserAvatar: avatar, LastLoginIp: ip, SessionKey: sessionKey}
|
||||
} else if result.Error == gorm.ErrRecordNotFound || result.RowsAffected == 0 { // BUG 始终不会返回 ErrRecordNotFound 的报错
|
||||
newUser := UsersModel{
|
||||
OpenId: openId,
|
||||
UserName: name,
|
||||
UserAvatar: avatar,
|
||||
LastLoginIp: ip,
|
||||
SessionKey: sessionKey,
|
||||
}
|
||||
if err := db.Create(&newUser).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
temp = &newUser // INFO 这里应该就是 GORM 插入后得到的对象。
|
||||
temp = &newUser // 这里是 GORM 插入后得到的对象。
|
||||
} else {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return temp, nil
|
||||
return
|
||||
}
|
||||
|
@ -16,6 +16,13 @@ type Response struct {
|
||||
}
|
||||
|
||||
func Code2Session(js_code string) (*Response, error) {
|
||||
|
||||
return &Response{ // TEST
|
||||
OpenId: "open_id",
|
||||
SessionKey: "session_key",
|
||||
Errcode: 0,
|
||||
Errmsg: "ok",
|
||||
}, nil
|
||||
appid := variable.ConfigYml.GetString("Weixin.AppId")
|
||||
appSecret := variable.ConfigYml.GetString("Weixin.AppSecret")
|
||||
grantType := variable.ConfigYml.GetString("Weixin.Code2Session.GrantType")
|
||||
|
@ -22,7 +22,7 @@ Token:
|
||||
IsCacheToRedis: 0 #用户token是否缓存到redis, 如果已经正确配置了redis,建议设置为1, 开启redis缓存token,(1=用户token缓存到redis; 0=token只存在于mysql)
|
||||
|
||||
Redis:
|
||||
Host: "127.0.0.1" # 服务器本地调用。 开发时也在本地启动一个。
|
||||
Host: "113.44.68.213" # 服务器本地调用。 开发时也在本地启动一个。
|
||||
Port: 6379
|
||||
Auth: ""
|
||||
MaxIdle: 10
|
||||
|
@ -84,9 +84,9 @@ func InitWebRouter() *gin.Engine {
|
||||
// 1.编写一个表单参数验证器结构体,参见代码: app/http/validator/web/users/register.go
|
||||
// 2.将以上表单参数验证器注册,遵守 键 =》值 格式注册即可 ,app/http/validator/common/register_validator/web_register_validator.go 20行就是注册时候的键 consts.ValidatorPrefix+"UsersRegister"
|
||||
// 3.按照注册时的键,直接从容器调用即可 :validatorFactory.Create(consts.ValidatorPrefix+"UsersRegister")
|
||||
noAuth.POST("register", validatorFactory.Create(consts.ValidatorPrefix+"UsersRegister"))
|
||||
// noAuth.POST("register", validatorFactory.Create(consts.ValidatorPrefix+"UsersRegister"))
|
||||
// 不需要验证码即可登陆
|
||||
noAuth.POST("login", validatorFactory.Create(consts.ValidatorPrefix+"UsersLogin"))
|
||||
// noAuth.POST("login", validatorFactory.Create(consts.ValidatorPrefix+"UsersLogin"))
|
||||
// 如果加载了验证码中间件,那么就需要提交验证码才可以登陆(本质上就是给登陆接口增加了2个参数:验证码id提交时的键:captcha_id 和 验证码值提交时的键 captcha_value,具体参见配置文件)
|
||||
//noAuth.Use(authorization.CheckCaptchaAuth()).POST("login", validatorFactory.Create(consts.ValidatorPrefix+"UsersLogin"))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user