This commit is contained in:
eson
2023-07-24 17:22:06 +08:00
parent 7ec78d1c35
commit a10e100364
23 changed files with 1004 additions and 137 deletions

View File

@@ -1,7 +1,6 @@
Name: home-user-auth
Host: 0.0.0.0
Port: 9904
MainAddress: "http://localhost:9900"
SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest
Auth:
@@ -9,14 +8,5 @@ Auth:
AccessExpire: 2592000
RefreshAfter: 1592000
OAuth:
google:
appid: "1064842923358-e94msq2glj6qr4lrva9ts3hqjjt53q8h.apps.googleusercontent.com"
secret: "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"
facebook:
appid: "1095953604597065"
secret: "b146872550a190d5275b1420c212002e"
Stripe:
SK: "sk_test_51IisojHygnIJZeghPVSBhkwySfcyDV4SoAduIxu3J7bvSJ9cZMD96LY1LO6SpdbYquLJX5oKvgEBB67KT9pecfCy00iEC4pp9y"

View File

@@ -12,16 +12,6 @@ import (
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodPost,
Path: "/api/user/login",
Handler: UserLoginHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/api/user/accept-cookie",
Handler: AcceptCookieHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/user/fonts",
@@ -67,16 +57,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/api/user/order-delete",
Handler: UserOderDeleteHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/user/oauth2/login/google",
Handler: UserGoogleLoginHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/user/oauth2/login/register",
Handler: UserEmailRegisterHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/user/order-list",

View File

@@ -1,35 +0,0 @@
package handler
import (
"net/http"
"reflect"
"fusenapi/utils/basic"
"fusenapi/server/home-user-auth/internal/logic"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
)
func UserEmailRegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.RequestEmailRegister
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
if err != nil {
return
}
// 创建一个业务逻辑层实例
l := logic.NewUserEmailRegisterLogic(r.Context(), svcCtx)
rl := reflect.ValueOf(l)
basic.BeforeLogic(w, r, rl)
resp := l.UserEmailRegister(&req, userinfo)
if !basic.AfterLogic(w, r, rl, resp) {
basic.NormalAfterLogic(w, r, resp)
}
}
}

View File

@@ -1,35 +0,0 @@
package handler
import (
"net/http"
"reflect"
"fusenapi/utils/basic"
"fusenapi/server/home-user-auth/internal/logic"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
)
func UserGoogleLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.RequestGoogleLogin
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
if err != nil {
return
}
// 创建一个业务逻辑层实例
l := logic.NewUserGoogleLoginLogic(r.Context(), svcCtx)
rl := reflect.ValueOf(l)
basic.BeforeLogic(w, r, rl)
resp := l.UserGoogleLogin(&req, userinfo)
if !basic.AfterLogic(w, r, rl, resp) {
basic.NormalAfterLogic(w, r, resp)
}
}
}

View File

@@ -1,35 +0,0 @@
package handler
import (
"net/http"
"reflect"
"fusenapi/utils/basic"
"fusenapi/server/home-user-auth/internal/logic"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
)
func UserLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.RequestUserLogin
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
if err != nil {
return
}
// 创建一个业务逻辑层实例
l := logic.NewUserLoginLogic(r.Context(), svcCtx)
rl := reflect.ValueOf(l)
basic.BeforeLogic(w, r, rl)
resp := l.UserLogin(&req, userinfo)
if !basic.AfterLogic(w, r, rl, resp) {
basic.NormalAfterLogic(w, r, resp)
}
}
}

View File

@@ -0,0 +1,161 @@
package logic
import (
"bytes"
"log"
"net/smtp"
"sync"
"text/template"
"time"
)
var EmailManager *EmailSender
// EmailSender
type EmailSender struct {
lock sync.Mutex
EmailTasks chan string // 处理email的队列
Auth smtp.Auth // 邮箱发送处理
FromEmail string // 发送的email, 公司email
emailSending map[string]*EmailTask // 正在发送的邮件
ResendTimeLimit time.Duration // 重发时间限制
}
// EmailTask
type EmailTask struct {
Email string // email
SendTime time.Time // 处理的任务时间
}
// ProcessEmailTasks 处理邮件队列
func (m *EmailSender) ProcessEmailTasks() {
for {
emailTarget, ok := <-m.EmailTasks
if !ok {
log.Println("Email task channel closed")
break
}
m.lock.Lock()
_, isSending := m.emailSending[emailTarget]
if isSending {
m.lock.Unlock()
continue
}
m.emailSending[emailTarget] = &EmailTask{
Email: emailTarget,
SendTime: time.Now(),
}
m.lock.Unlock()
// TODO: Replace with actual email content
content := []byte("Hello, this is a test email")
err := smtp.SendMail(emailTarget, m.Auth, m.FromEmail, []string{emailTarget}, content)
if err != nil {
log.Printf("Failed to send email to %s: %v\n", emailTarget, err)
m.Resend(emailTarget, content)
}
}
}
// Resend 重发邮件
func (m *EmailSender) Resend(emailTarget string, content []byte) {
time.Sleep(m.ResendTimeLimit)
m.lock.Lock()
defer m.lock.Unlock()
// Check if the email task still exists and has not been sent successfully
if task, ok := m.emailSending[emailTarget]; ok && task.SendTime.Add(m.ResendTimeLimit).After(time.Now()) {
err := smtp.SendMail(emailTarget, m.Auth, m.FromEmail, []string{emailTarget}, content)
if err != nil {
log.Printf("Failed to resend email to %s: %v\n", emailTarget, err)
} else {
delete(m.emailSending, emailTarget)
}
}
}
// ClearExpiredTasks 清除过期的邮件任务
func (m *EmailSender) ClearExpiredTasks() {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
<-ticker.C
m.lock.Lock()
for email, task := range m.emailSending {
if task.SendTime.Add(m.ResendTimeLimit).Before(time.Now()) {
delete(m.emailSending, email)
}
}
m.lock.Unlock()
}
}
func init() {
// Initialize the email manager
EmailManager = &EmailSender{
EmailTasks: make(chan string, 10),
Auth: smtp.PlainAuth(
"",
"user@example.com",
"password",
"smtp.gmail.com",
),
FromEmail: "user@example.com",
emailSending: make(map[string]*EmailTask, 10),
ResendTimeLimit: time.Minute * 1,
}
// Start processing email tasks
go EmailManager.ProcessEmailTasks()
// Start clearing expired tasks
go EmailManager.ClearExpiredTasks()
}
const emailTemplate = `Subject: Your {{.CompanyName}} Account Confirmation
Dear
Thank you for creating an account with {{.CompanyName}}. We're excited to have you on board!
Before we get started, we just need to confirm that this is the right email address. Please confirm your email address by clicking on the link below:
{{.ConfirmationLink}}
Once you've confirmed, you can get started with {{.CompanyName}}. If you have any questions, feel free to reply to this email. We're here to help!
If you did not create an account with us, please ignore this email.
Thanks,
{{.SenderName}}
{{.SenderTitle}}
{{.CompanyName}}
`
func RenderEmailTemplate(companyName, recipient, confirmationLink, senderName, senderTitle string) string {
tmpl, err := template.New("email").Parse(emailTemplate)
if err != nil {
log.Fatal(err)
}
data := map[string]string{
"CompanyName": companyName,
"ConfirmationLink": confirmationLink,
"SenderName": senderName,
"SenderTitle": senderTitle,
}
var result bytes.Buffer
err = tmpl.Execute(&result, data)
if err != nil {
log.Fatal(err)
}
return result.String()
}

View File

@@ -1,42 +0,0 @@
package logic
import (
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"context"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type UserEmailRegisterLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUserEmailRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserEmailRegisterLogic {
return &UserEmailRegisterLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// 处理进入前逻辑w,r
// func (l *UserEmailRegisterLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// }
// 处理逻辑后 w,r 如:重定向
// func (l *UserEmailRegisterLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
// }
func (l *UserEmailRegisterLogic) UserEmailRegister(req *types.RequestEmailRegister, userinfo *auth.UserInfo) (resp *basic.Response) {
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
// userinfo 传入值时, 一定不为null
return resp.SetStatus(basic.CodeOK)
}

View File

@@ -6,7 +6,6 @@ import (
"fusenapi/utils/basic"
"log"
"net/http"
"net/url"
"time"
"context"
@@ -16,6 +15,7 @@ import (
"github.com/474420502/requests"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
"golang.org/x/net/proxy"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
@@ -31,6 +31,7 @@ type UserGoogleLoginLogic struct {
isRegistered bool // 是否注册
registerToken string // 注册邮箱的token
oauthinfo *auth.OAuthInfo
}
func NewUserGoogleLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserGoogleLoginLogic {
@@ -47,29 +48,52 @@ func NewUserGoogleLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *U
func (l *UserGoogleLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
rurl := fmt.Sprintf(
l.svcCtx.Config.MainAddress+"/oauth?token=%s&is_registered=%t&register_token=%s",
l.token,
l.isRegistered,
l.registerToken,
)
if resp.Code == 200 {
html := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head>
<title>Redirect</title>
<script type="text/javascript">
window.onload = function() {
window.location = "%s";
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)
}
</script>
</head>
<body>
</body>
</html>
`, rurl)
fmt.Fprintln(w, html)
l.registerToken = rtoken
}
rurl := fmt.Sprintf(
l.svcCtx.Config.MainAddress+"/oauth?token=%s&is_registered=%t&register_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)
}
}
func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, userinfo *auth.UserInfo) (resp *basic.Response) {
@@ -114,36 +138,39 @@ func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, us
log.Println(r.Json())
googleId := r.Json().Get("id").Int()
// l.redirectUrl = "http://localhost:9900/oauth?token=21321123&is_registered"
// return resp.Set(304, "21321321")
user, err := l.svcCtx.AllModels.FsUser.FindUserByGoogleId(context.TODO(), googleId)
log.Println(user)
if err != nil {
if err != gorm.ErrRecordNotFound {
logx.Error(err)
return resp.SetStatus(basic.CodeDbSqlErr)
}
// 进入邮件注册流程
if req.Email == "" {
return resp.SetStatus(basic.CodeOK)
}
// 如果密码匹配,则生成 JWT Token。
nowSec := time.Now().Unix()
jwtToken, err := auth.GenerateJwtToken(&l.svcCtx.Config.Auth.AccessSecret, l.svcCtx.Config.Auth.AccessExpire, nowSec, 0, 0)
// 这里是注册模块, 发邮件, 通过邮件注册确认邮箱存在
// 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeServiceErr)
// 邮箱验证格式错误
if !auth.ValidateEmail(req.Email) {
return resp.SetStatus(basic.CodeOAuthEmailErr)
}
return resp.SetRewriteHandler(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "http://localhost:9900?token="+url.QueryEscape(jwtToken), http.StatusFound)
})
return resp.SetStatus(basic.CodeOK)
}
// 如果密码匹配,则生成 JWT Token。
nowSec := time.Now().Unix()
jwtToken, err := auth.GenerateJwtToken(&l.svcCtx.Config.Auth.AccessSecret, l.svcCtx.Config.Auth.AccessExpire, nowSec, user.Id, 0)
// 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeServiceErr)
}
l.token = jwtToken
return resp.SetStatus(basic.CodeOK)
}

View File

@@ -70,19 +70,6 @@ type Product struct {
IsStop int64 `json:"is_stop"`
}
type RequestGoogleLogin struct {
Code string `form:"code"`
Scope string `form:"scope"`
AuthUser string `form:"authuser"`
Prompt string `form:"prompt"`
Email string `form:"email,optional"`
}
type RequestEmailRegister struct {
Email string `json:"email"`
RegisterToken string `json:"register_token"`
}
type RequestContactService struct {
Type string `json:"type"` // 类型
RelationID int64 `json:"relation_id"` // 关系id
@@ -108,11 +95,6 @@ type RequestBasicInfoForm struct {
IsRemoveBg int64 `json:"is_remove_bg"` // 用户上传logo是否去除背景
}
type RequestUserLogin struct {
Email string `json:"email"`
Password string `json:"password"`
}
type RequestAddAddress struct {
Id int64 `json:"id"` // address_id 地址id
IsDefault int64 `json:"is_default"` //是否默认