for save
This commit is contained in:
parent
abc315a39b
commit
0ce35645b2
|
@ -39,7 +39,7 @@ func (u *FsUserModel) FindUserByGoogleId(ctx context.Context, Id int64) (resp *F
|
|||
return resp, err
|
||||
}
|
||||
|
||||
func (u *FsUserModel) TransactionRegsterGoogle(ctx context.Context, fc func(tx *gorm.DB) error) (err error) {
|
||||
func (u *FsUserModel) Transaction(ctx context.Context, fc func(tx *gorm.DB) error) (err error) {
|
||||
return u.db.WithContext(ctx).Transaction(fc)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,40 +11,49 @@ import (
|
|||
|
||||
var EmailManager *EmailSender
|
||||
|
||||
type EmailFormat struct {
|
||||
TargetEmail string // 发送的目标email
|
||||
CompanyName string // fs公司名
|
||||
ConfirmationLink string // fs确认连接
|
||||
SenderName string // 发送人
|
||||
SenderTitle string // 发送标题
|
||||
}
|
||||
|
||||
// EmailSender
|
||||
type EmailSender struct {
|
||||
lock sync.Mutex
|
||||
EmailTasks chan string
|
||||
EmailTasks chan *EmailFormat
|
||||
Auth smtp.Auth
|
||||
FromEmail string
|
||||
emailSending map[string]*EmailTask
|
||||
ResendTimeLimit time.Duration
|
||||
semaphore chan struct{}
|
||||
|
||||
emailSending map[string]*EmailTask
|
||||
semaphore chan struct{}
|
||||
}
|
||||
|
||||
// EmailTask
|
||||
type EmailTask struct {
|
||||
Email string // email
|
||||
SendTime time.Time // 处理的任务时间
|
||||
Email *EmailFormat // email
|
||||
SendTime time.Time // 处理的任务时间
|
||||
}
|
||||
|
||||
func (m *EmailSender) ProcessEmailTasks() {
|
||||
for {
|
||||
emailTarget, ok := <-m.EmailTasks
|
||||
emailformat, ok := <-m.EmailTasks
|
||||
if !ok {
|
||||
log.Println("Email task channel closed")
|
||||
break
|
||||
}
|
||||
|
||||
m.lock.Lock()
|
||||
_, isSending := m.emailSending[emailTarget]
|
||||
_, isSending := m.emailSending[emailformat.TargetEmail]
|
||||
if isSending {
|
||||
m.lock.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
m.emailSending[emailTarget] = &EmailTask{
|
||||
Email: emailTarget,
|
||||
m.emailSending[emailformat.TargetEmail] = &EmailTask{
|
||||
Email: emailformat,
|
||||
SendTime: time.Now(),
|
||||
}
|
||||
m.lock.Unlock()
|
||||
|
@ -55,11 +64,11 @@ func (m *EmailSender) ProcessEmailTasks() {
|
|||
go func() {
|
||||
defer func() { <-m.semaphore }() // Release a token
|
||||
|
||||
content := RenderEmailTemplate("fusen", "http://www.baidu.com", "fusen@gmail.com", "mail-valid")
|
||||
err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailTarget}, content)
|
||||
content := RenderEmailTemplate(emailformat.CompanyName, emailformat.ConfirmationLink, emailformat.SenderName, emailformat.SenderTitle)
|
||||
err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailformat.TargetEmail}, content)
|
||||
if err != nil {
|
||||
log.Printf("Failed to send email to %s: %v\n", emailTarget, err)
|
||||
m.Resend(emailTarget, content)
|
||||
log.Printf("Failed to send email to %s: %v\n", emailformat, err)
|
||||
m.Resend(emailformat.TargetEmail, content)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -105,7 +114,7 @@ func init() {
|
|||
|
||||
// Initialize the email manager
|
||||
EmailManager = &EmailSender{
|
||||
EmailTasks: make(chan string, 10),
|
||||
EmailTasks: make(chan *EmailFormat, 10),
|
||||
Auth: smtp.PlainAuth(
|
||||
"",
|
||||
"user@example.com",
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
|
@ -10,6 +12,7 @@ import (
|
|||
"fusenapi/server/auth/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserEmailConfirmationLogic struct {
|
||||
|
@ -34,6 +37,43 @@ func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEma
|
|||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
|
||||
token, err := l.svcCtx.TokenManger.Decrypt(req.Token)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
|
||||
}
|
||||
|
||||
switch token.OperateType {
|
||||
case auth.OpTypeRegister:
|
||||
if time.Since(token.CreateAt) >= 24*time.Hour {
|
||||
return resp.SetStatus(basic.CodeOAuthConfirmationTimeoutErr)
|
||||
}
|
||||
|
||||
switch token.Platform {
|
||||
case "google":
|
||||
|
||||
user, err := l.OpGoogleRegister(token)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeDbSqlErr)
|
||||
}
|
||||
|
||||
jwtToken, err := auth.GenerateJwtTokenUint64(
|
||||
auth.StringToHash(*user.PasswordHash),
|
||||
l.svcCtx.Config.Auth.AccessExpire,
|
||||
time.Now().Unix(),
|
||||
user.Id,
|
||||
0,
|
||||
)
|
||||
|
||||
case "facebook":
|
||||
l.OpGoogleRegister(token)
|
||||
}
|
||||
|
||||
default:
|
||||
return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
|
||||
}
|
||||
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
||||
|
||||
|
@ -41,3 +81,41 @@ func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEma
|
|||
// func (l *UserEmailConfirmationLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
// OpGoogleRegister 谷歌平台的注册流程
|
||||
func (l *UserEmailConfirmationLogic) OpGoogleRegister(token *auth.RegisterToken) (*gmodel.FsUser, error) {
|
||||
user := &gmodel.FsUser{}
|
||||
|
||||
err := l.svcCtx.AllModels.FsUser.Transaction(context.TODO(), func(tx *gorm.DB) error {
|
||||
|
||||
err := tx.Model(user).Where("email = ?", token.Email).Take(user).Error
|
||||
if err != nil {
|
||||
// 没有找到在数据库就创建注册
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
createAt := time.Now().Unix()
|
||||
user.Email = &token.Email
|
||||
user.CreatedAt = &createAt
|
||||
user.GoogleId = &token.Id
|
||||
user.PasswordHash = &token.Password
|
||||
err = tx.Model(user).Create(user).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 继承guest_id的资源表
|
||||
// tx.Table("fs_resources").Model()
|
||||
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
user.GoogleId = &token.Id
|
||||
return tx.Model(user).Update("google_id", user).Error
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
|
|
@ -39,5 +39,42 @@ func (l *UserEmailRegisterLogic) UserEmailRegister(req *types.RequestEmailRegist
|
|||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
|
||||
// 进入邮件注册流程
|
||||
// 这里是注册模块, 发邮件, 通过邮件注册确认邮箱存在
|
||||
|
||||
// 邮箱验证格式错误
|
||||
if !auth.ValidateEmail(req.Email) {
|
||||
return resp.SetStatus(basic.CodeOAuthEmailErr)
|
||||
}
|
||||
|
||||
token, err := l.svcCtx.TokenManger.Decrypt(req.RegisterToken)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
|
||||
}
|
||||
|
||||
if token.OperateType != auth.OpTypeRegister {
|
||||
return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
|
||||
}
|
||||
|
||||
// 确认email 重新序列化
|
||||
token.Email = req.Email
|
||||
token.WCId = req.WCId
|
||||
|
||||
clurl, err := l.svcCtx.TokenManger.Generate(token)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
|
||||
}
|
||||
|
||||
// 进入发送邮箱的系统
|
||||
EmailManager.EmailTasks <- &EmailFormat{
|
||||
TargetEmail: req.Email,
|
||||
CompanyName: "fusen",
|
||||
ConfirmationLink: clurl,
|
||||
SenderName: "support@fusenpack.com",
|
||||
SenderTitle: "register-valid",
|
||||
} // email进入队
|
||||
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
@ -46,52 +49,6 @@ func NewUserGoogleLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *U
|
|||
// func (l *UserGoogleLoginLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
<<<<<<< HEAD
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
func (l *UserGoogleLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
|
||||
if resp.Code == 200 {
|
||||
|
||||
if !l.isRegistered {
|
||||
|
||||
rtoken, err := l.svcCtx.TokenManger.Encrypt(l.registerInfo)
|
||||
if err != nil {
|
||||
resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
|
||||
}
|
||||
l.registerToken = rtoken
|
||||
}
|
||||
|
||||
rurl := fmt.Sprintf(
|
||||
l.svcCtx.Config.MainAddress+"/oauth?token=%s&is_registered=%t®ister_token=%s",
|
||||
l.token,
|
||||
l.isRegistered,
|
||||
l.registerToken,
|
||||
)
|
||||
|
||||
html := fmt.Sprintf(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Redirect</title>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
window.location = "%s";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
`, rurl)
|
||||
fmt.Fprintln(w, html)
|
||||
} else {
|
||||
httpx.OkJson(w, resp)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 529a02ac7582babc634e6f9cddab126f5f962efb
|
||||
func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
|
@ -141,30 +98,34 @@ func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, us
|
|||
return resp.SetStatus(basic.CodeDbSqlErr)
|
||||
}
|
||||
|
||||
nonce := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
||||
|
||||
l.registerInfo = &auth.RegisterToken{
|
||||
Id: googleId,
|
||||
Password: base64.URLEncoding.EncodeToString(nonce),
|
||||
Platform: "google",
|
||||
OperateType: auth.OpTypeRegister,
|
||||
CreateAt: time.Now(),
|
||||
}
|
||||
|
||||
l.isRegistered = false
|
||||
l.registerToken = //
|
||||
|
||||
// 进入邮件注册流程
|
||||
// if req.Email == "" {
|
||||
// return resp.SetStatus(basic.CodeOK)
|
||||
// }
|
||||
|
||||
// // 这里是注册模块, 发邮件, 通过邮件注册确认邮箱存在
|
||||
|
||||
// // 邮箱验证格式错误
|
||||
// if !auth.ValidateEmail(req.Email) {
|
||||
// return resp.SetStatus(basic.CodeOAuthEmailErr)
|
||||
// }
|
||||
|
||||
// EmailManager.EmailTasks <- req.Email // email进入队
|
||||
token, err := l.svcCtx.TokenManger.Encrypt(l.registerInfo)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
|
||||
}
|
||||
l.registerToken = token
|
||||
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
||||
|
||||
l.isRegistered = true
|
||||
// 如果密码匹配,则生成 JWT Token。
|
||||
nowSec := time.Now().Unix()
|
||||
|
||||
jwtToken, err := auth.GenerateJwtTokenUint64(auth.StringToHash(*user.PasswordHash), l.svcCtx.Config.Auth.AccessExpire, nowSec, user.Id, 0)
|
||||
jwtToken, err := auth.GenerateJwtTokenUint64(auth.StringToHash(*user.PasswordHash), l.svcCtx.Config.Auth.AccessExpire, time.Now().Unix(), user.Id, 0)
|
||||
|
||||
// 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。
|
||||
if err != nil {
|
||||
|
@ -182,23 +143,6 @@ func (l *UserGoogleLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request
|
|||
|
||||
if resp.Code == 200 {
|
||||
|
||||
if !l.isRegistered {
|
||||
now := time.Now()
|
||||
rtoken, err := auth.GenerateRegisterToken(
|
||||
&l.svcCtx.Config.Auth.AccessSecret,
|
||||
l.svcCtx.Config.Auth.AccessExpire,
|
||||
now.Unix(),
|
||||
l.oauthinfo.Id,
|
||||
l.oauthinfo.Platform,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
|
||||
}
|
||||
|
||||
l.registerToken = rtoken
|
||||
}
|
||||
|
||||
rurl := fmt.Sprintf(
|
||||
l.svcCtx.Config.MainAddress+"/oauth?token=%s&is_registered=%t®ister_token=%s",
|
||||
l.token,
|
||||
|
|
|
@ -18,12 +18,13 @@ type RequestGoogleLogin struct {
|
|||
}
|
||||
|
||||
type RequestEmailConfirmation struct {
|
||||
Email string `json:"email"` // 要确认的email
|
||||
Token string `json:"token"` // 操作Token
|
||||
Token string `query:"token"` // 操作Token
|
||||
}
|
||||
|
||||
type RequestEmailRegister struct {
|
||||
Email string `json:"email"`
|
||||
WCId uint64 `json:"wcid"`
|
||||
GuestId uint64 `json:"guest_id"`
|
||||
RegisterToken string `json:"register_token"`
|
||||
}
|
||||
|
||||
|
|
|
@ -12,16 +12,16 @@ import "basic.api"
|
|||
service auth {
|
||||
@handler UserLoginHandler
|
||||
post /api/auth/login(RequestUserLogin) returns (response);
|
||||
|
||||
|
||||
@handler AcceptCookieHandler
|
||||
post /api/auth/accept-cookie(request) returns (response);
|
||||
|
||||
|
||||
@handler UserGoogleLoginHandler
|
||||
get /api/auth/oauth2/login/google(RequestGoogleLogin) returns (response);
|
||||
|
||||
|
||||
@handler UserEmailConfirmationHandler
|
||||
get /api/auth/email/confirmation(RequestEmailConfirmation) returns (response);
|
||||
|
||||
|
||||
@handler UserEmailRegisterHandler
|
||||
get /api/auth/oauth2/register(RequestEmailRegister) returns (response);
|
||||
}
|
||||
|
@ -40,12 +40,13 @@ type RequestGoogleLogin {
|
|||
}
|
||||
|
||||
type RequestEmailConfirmation {
|
||||
Email string `json:"email"` // 要确认的email
|
||||
Token string `json:"token"` // 操作Token
|
||||
Token string `query:"token"` // 操作Token
|
||||
}
|
||||
|
||||
type RequestEmailRegister {
|
||||
Email string `json:"email"`
|
||||
WCId uint64 `json:"wcid"`
|
||||
GuestId uint64 `json:"guest_id"`
|
||||
RegisterToken string `json:"register_token"`
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,12 @@ import (
|
|||
"net/url"
|
||||
)
|
||||
|
||||
type OperateType int8
|
||||
|
||||
const (
|
||||
OpTypeRegister OperateType = 1 //注册的操作类型
|
||||
)
|
||||
|
||||
type ConfirmationLink[T any] struct {
|
||||
Secret []byte
|
||||
DefaultQueryKey string // 默认key 是 token
|
||||
|
|
|
@ -13,10 +13,14 @@ import (
|
|||
)
|
||||
|
||||
type RegisterToken struct {
|
||||
Id int64
|
||||
Password string
|
||||
Platform string
|
||||
Expired time.Time
|
||||
OperateType // 操作的类型, 验证的token 必须要继承这个
|
||||
Id int64 // 注册的 id
|
||||
GuestId uint64 // guest_id 需要继承
|
||||
WCId uint64 // websocket 通道id
|
||||
Email string // email
|
||||
Password string // 密码
|
||||
Platform string // 平台
|
||||
CreateAt time.Time // 创建时间
|
||||
}
|
||||
|
||||
func ParseJwtTokenUint64SecretByRequest(r *http.Request, AccessSecret uint64) (jwt.MapClaims, error) {
|
||||
|
|
|
@ -39,9 +39,11 @@ var (
|
|||
CodeServiceErr = &StatusResponse{510, "server logic error"} // 服务逻辑错误
|
||||
CodeUnAuth = &StatusResponse{401, "unauthorized"} // 未授权
|
||||
|
||||
CodeOAuthGoogleApiErr = &StatusResponse{5070, "oauth2 google api error"}
|
||||
CodeOAuthRegisterTokenErr = &StatusResponse{5071, "oauth2 jwt token error"}
|
||||
CodeOAuthEmailErr = &StatusResponse{5071, "Invalid email format"}
|
||||
CodeOAuthGoogleApiErr = &StatusResponse{5070, "oauth2 google api error"}
|
||||
CodeOAuthRegisterTokenErr = &StatusResponse{5071, "oauth2 register create token error"}
|
||||
CodeOAuthEmailErr = &StatusResponse{5072, "Invalid email format"}
|
||||
CodeOAuthRandReaderErr = &StatusResponse{5073, "rand reader error"}
|
||||
CodeOAuthConfirmationTimeoutErr = &StatusResponse{5074, "confirmation timeout error"}
|
||||
|
||||
CodeS3PutObjectRequestErr = &StatusResponse{5060, "s3 PutObjectRequest error"} // s3 PutObjectRequest 错误
|
||||
CodeS3PutSizeLimitErr = &StatusResponse{5061, "s3 over limit size error"} // s3 超过文件大小限制 错误
|
||||
|
@ -75,7 +77,8 @@ var (
|
|||
CodeAesCbcEncryptionErr = &StatusResponse{5106, "encryption data err"} // 加密数据失败
|
||||
CodeAesCbcDecryptionErr = &StatusResponse{5107, "decryption data err"} // 解密数据失败
|
||||
|
||||
CodeSharedStateErr = &StatusResponse{5201, "shared state server err"} // 状态机错误
|
||||
CodeSharedStateErr = &StatusResponse{5201, "shared state server err"} // 状态机错误
|
||||
CodeEmailConfirmationErr = &StatusResponse{5202, "email confirmation err"} // email 验证错误
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
|
|
Loading…
Reference in New Issue
Block a user