package logic import ( "crypto/rand" "encoding/base64" "fmt" "fusenapi/utils/auth" "fusenapi/utils/basic" "io" "log" "net/http" "time" "context" "fusenapi/server/auth/internal/svc" "fusenapi/server/auth/internal/types" "github.com/474420502/requests" "github.com/google/uuid" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/rest/httpx" "golang.org/x/oauth2" "golang.org/x/oauth2/google" "gorm.io/gorm" ) type UserGoogleLoginLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext token string // 登录 token isRegistered bool // 是否注册 registerToken string // 注册邮箱的token registerInfo *auth.RegisterToken } func NewUserGoogleLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserGoogleLoginLogic { return &UserGoogleLoginLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } // 处理进入前逻辑w,r // func (l *UserGoogleLoginLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) { // } func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, userinfo *auth.UserInfo) (resp *basic.Response) { // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) // userinfo 传入值时, 一定不为null var googleOauthConfig = &oauth2.Config{ RedirectURL: fmt.Sprintf("%s/api/auth/oauth2/login/google", l.svcCtx.Config.MainAddress), ClientID: l.svcCtx.Config.OAuth.Google.Appid, ClientSecret: l.svcCtx.Config.OAuth.Google.Secret, Scopes: []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"}, Endpoint: google.Endpoint, } token, err := googleOauthConfig.Exchange(l.ctx, req.Code) if err != nil { logx.Error(err) return resp.SetStatus(basic.CodeApiErr, err.Error()) } r, err := requests.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token.AccessToken).Execute() if err != nil { logx.Error(err) return resp.SetStatus(basic.CodeOAuthGoogleApiErr, err.Error()) } log.Println(r.Json()) googleId := r.Json().Get("id").Int() user, err := l.svcCtx.AllModels.FsUser.FindUserByGoogleId(context.TODO(), googleId) if err != nil { if err != gorm.ErrRecordNotFound { logx.Error(err) 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{ Password: base64.RawURLEncoding.EncodeToString(nonce), Platform: string(auth.PLATFORM_GOOGLE), OperateType: auth.OpTypeRegister, TraceId: uuid.NewString(), CreateAt: time.Now().UTC(), Extend: map[string]interface{}{ "google_id": googleId, }, } l.isRegistered = false token, err := l.svcCtx.OAuthTokenManger.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。 jwtToken, err := auth.GenerateJwtTokenUint64(auth.StringToHash(*user.PasswordHash), l.svcCtx.Config.Auth.AccessExpire, time.Now().UTC().Unix(), user.Id, 0) // 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。 if err != nil { logx.Error(err) return resp.SetStatus(basic.CodeServiceErr) } l.token = jwtToken return resp.SetStatus(basic.CodeOK) } // 处理逻辑后 w,r 如:重定向, resp 必须重新处理 func (l *UserGoogleLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) { if resp.Code == 200 { rurl := fmt.Sprintf( "http://www.fusen.3718.cn"+"/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) } }