diff --git a/README.md b/README.md index beb47f5..9d4869a 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ #### 登录 -- 添加了`github`登录,需要在后台配置好就可以用了,具体可看后台OAuth配置 -- 添加了web后台授权登录 +- 添加了`github`和`google`授权登录,需要在后台配置好就可以用了,具体可看后台OAuth配置 +- 添加了web后台授权登录,点击后直接登录后台就自动登录客户端了 ![pc_login](docs/pc_login.png) @@ -66,7 +66,7 @@ ![web_admin_gr](docs/web_admin_gr.png) 5. 可以直接打开webclient,方便使用 ![web_webclient](docs/admin_webclient.png) -6. Oauth,暂时只支持了`Github`, 需要创建一个`OAuth App`,然后配置到后台 +6. Oauth,暂时只支持了`Github`和`Google`, 需要创建一个`OAuth App`,然后配置到后台 ![web_admin_oauth](docs/web_admin_oauth.png) - `github oauth app`在`Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App` 中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers) diff --git a/README_EN.md b/README_EN.md index 9894d21..c2b0db5 100644 --- a/README_EN.md +++ b/README_EN.md @@ -36,7 +36,7 @@ desktop software that provides self-hosted solutions. #### Login -- Added `GitHub` login, which can be used after configuration in the admin panel. See the OAuth configuration section +- Added `GitHub` and `Google` login, which can be used after configuration in the admin panel. See the OAuth configuration section for details. - Added authorization login for the web admin panel. @@ -67,7 +67,7 @@ installation are `admin` `admin`, please change the password immediately.*** ![web_admin_gr](docs/web_admin_gr.png) 5. You can open the web client directly for convenience: ![web_webclient](docs/admin_webclient.png) -6. OAuth support: Currently, only `GitHub` is supported. You need to create an `OAuth App` and configure it in the admin +6. OAuth support: Currently, `GitHub` and `Google` is supported. You need to create an `OAuth App` and configure it in the admin panel. ![web_admin_oauth](docs/web_admin_oauth.png) - Create a `GitHub OAuth App` diff --git a/conf/config.yaml b/conf/config.yaml index 021a331..547b83a 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -19,7 +19,7 @@ rustdesk: key: "123456789" logger: path: "./runtime/log.txt" - level: "error" #trace,debug,info,warn,error,fatal + level: "warn" #trace,debug,info,warn,error,fatal report-caller: true redis: addr: "127.0.0.1:6379" diff --git a/docs/pc_login.png b/docs/pc_login.png index acc8c1c..35088a7 100644 Binary files a/docs/pc_login.png and b/docs/pc_login.png differ diff --git a/docs/web_admin_oauth.png b/docs/web_admin_oauth.png index 05ea579..6c640dd 100644 Binary files a/docs/web_admin_oauth.png and b/docs/web_admin_oauth.png differ diff --git a/http/controller/api/ouath.go b/http/controller/api/ouath.go index 6744119..b586566 100644 --- a/http/controller/api/ouath.go +++ b/http/controller/api/ouath.go @@ -10,6 +10,7 @@ import ( "github.com/gin-gonic/gin" "net/http" "strconv" + "strings" ) type Oauth struct { @@ -161,9 +162,8 @@ func (o *Oauth) OauthCallback(c *gin.Context) { } c.String(http.StatusOK, "绑定成功") return - } - //登录 - if ac == service.OauthActionTypeLogin { + } else if ac == service.OauthActionTypeLogin { + //登录 if v.UserId != 0 { c.String(http.StatusInternalServerError, "授权已经成功") return @@ -181,7 +181,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) { } //自动注册 - u = service.AllService.UserService.RegisterByGithub(userData.Login, int64(userData.Id)) + u = service.AllService.UserService.RegisterByGithub(userData.Login, strconv.Itoa(userData.Id)) if u.Id == 0 { c.String(http.StatusInternalServerError, "注册失败") return @@ -193,19 +193,76 @@ func (o *Oauth) OauthCallback(c *gin.Context) { c.String(http.StatusOK, "授权成功") return } - //返回js c.Header("Content-Type", "text/html; charset=utf-8") c.String(http.StatusOK, "授权错误") - //up := &apiResp.UserPayload{} - //c.JSON(http.StatusOK, apiResp.LoginRes{ - // AccessToken: ut.Token, - // Type: "access_token", - // User: *up.FromUser(u), - //}) } + if ty == model.OauthTypeGoogle { + code := c.Query("code") + err, userData := service.AllService.OauthService.GoogleCallback(code) + if err != nil { + c.String(http.StatusInternalServerError, "授权失败:"+err.Error()) + return + } + //将空格替换成_ + googleName := strings.Replace(userData.Name, " ", "_", -1) + if ac == service.OauthActionTypeBind { + //fmt.Println("bind", ty, userData) + utr := service.AllService.OauthService.UserThirdInfo(ty, userData.Email) + if utr.UserId > 0 { + c.String(http.StatusInternalServerError, "已经绑定其他账号") + return + } + //绑定 + u := service.AllService.UserService.InfoById(v.UserId) + if u == nil { + c.String(http.StatusInternalServerError, "用户不存在") + return + } + //绑定 + err = service.AllService.OauthService.BindGoogleUser(userData.Email, googleName, v.UserId) + if err != nil { + c.String(http.StatusInternalServerError, "绑定失败") + return + } + c.String(http.StatusOK, "绑定成功") + return + } else if ac == service.OauthActionTypeLogin { + if v.UserId != 0 { + c.String(http.StatusInternalServerError, "授权已经成功") + return + } + u := service.AllService.UserService.InfoByGoogleEmail(userData.Email) + if u == nil { + oa := service.AllService.OauthService.InfoByOp(ty) + if !*oa.AutoRegister { + //c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定") + + v.ThirdName = googleName + v.ThirdOpenId = userData.Email + url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey + c.Redirect(http.StatusFound, url) + return + } + + //自动注册 + u = service.AllService.UserService.RegisterByGoogle(googleName, userData.Email) + if u.Id == 0 { + c.String(http.StatusInternalServerError, "注册失败") + return + } + } + + v.UserId = u.Id + service.AllService.OauthService.SetOauthCache(cacheKey, v, 0) + c.String(http.StatusOK, "授权成功") + return + } + } + c.String(http.StatusInternalServerError, "授权配置错误,请联系管理员") + } // WebOauthLogin diff --git a/service/oauth.go b/service/oauth.go index 19f4352..47b9c34 100644 --- a/service/oauth.go +++ b/service/oauth.go @@ -68,7 +68,15 @@ type GithubUserdata struct { UpdatedAt time.Time `json:"updated_at"` Url string `json:"url"` } - +type GoogleUserdata struct { + Email string `json:"email"` + FamilyName string `json:"family_name"` + GivenName string `json:"given_name"` + Id string `json:"id"` + Name string `json:"name"` + Picture string `json:"picture"` + VerifiedEmail bool `json:"verified_email"` +} type OauthCacheItem struct { UserId uint `json:"user_id"` Id string `json:"id"` //rustdesk的设备ID @@ -187,7 +195,7 @@ func (os *OauthService) GithubCallback(code string) (error error, userData *Gith }(resp.Body) // 在这里处理 GitHub 用户信息 - if err := json.NewDecoder(resp.Body).Decode(&userData); err != nil { + if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil { global.Logger.Warn("failed decoding user info: %s\n", err) error = errors.New("解析user info失败") return @@ -195,6 +203,37 @@ func (os *OauthService) GithubCallback(code string) (error error, userData *Gith return } +func (os *OauthService) GoogleCallback(code string) (error error, userData *GoogleUserdata) { + err, oauthConfig := os.GetOauthConfig(model.OauthTypeGoogle) + token, err := oauthConfig.Exchange(context.Background(), code) + if err != nil { + global.Logger.Warn(fmt.Printf("oauthConfig.Exchange() failed: %s\n", err)) + error = errors.New("获取token失败") + return + } + // 创建 HTTP 客户端,并将 access_token 添加到 Authorization 头中 + client := oauthConfig.Client(context.Background(), token) + resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo") + if err != nil { + global.Logger.Warn("failed getting user info: %s\n", err) + error = errors.New("获取user info失败: " + err.Error()) + return + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + global.Logger.Warn("failed closing response body: %s\n", err) + } + }(resp.Body) + + if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil { + global.Logger.Warn("failed decoding user info: %s\n", err) + error = errors.New("解析user info失败:" + err.Error()) + return + } + return +} + func (os *OauthService) UserThirdInfo(op, openid string) *model.UserThird { ut := &model.UserThird{} global.DB.Where("open_id = ? and third_type = ?", openid, op).First(ut) @@ -202,14 +241,22 @@ func (os *OauthService) UserThirdInfo(op, openid string) *model.UserThird { } func (os *OauthService) BindGithubUser(openid, username string, userId uint) error { + return os.BindOauthUser(model.OauthTypeGithub, openid, username, userId) +} + +func (os *OauthService) BindGoogleUser(email, username string, userId uint) error { + return os.BindOauthUser(model.OauthTypeGoogle, email, username, userId) +} +func (os *OauthService) BindOauthUser(thirdType, openid, username string, userId uint) error { utr := &model.UserThird{ OpenId: openid, - ThirdType: model.OauthTypeGithub, + ThirdType: thirdType, ThirdName: username, UserId: userId, } return global.DB.Create(utr).Error } + func (os *OauthService) UnBindGithubUser(userid uint) error { return global.DB.Where("user_id = ? and third_type = ?", userid, model.OauthTypeGithub).Delete(&model.UserThird{}).Error } diff --git a/service/user.go b/service/user.go index aeff619..86d1cae 100644 --- a/service/user.go +++ b/service/user.go @@ -175,7 +175,17 @@ func (us *UserService) RouteNames(u *model.User) []string { // InfoByGithubId 根据githubid取用户信息 func (us *UserService) InfoByGithubId(githubId string) *model.User { - ut := AllService.OauthService.UserThirdInfo(model.OauthTypeGithub, githubId) + return us.InfoByOauthId(model.OauthTypeGithub, githubId) +} + +// InfoByGoogleEmail 根据googleid取用户信息 +func (us *UserService) InfoByGoogleEmail(email string) *model.User { + return us.InfoByOauthId(model.OauthTypeGithub, email) +} + +// InfoByOauthId 根据oauth取用户信息 +func (us *UserService) InfoByOauthId(thirdType, uid string) *model.User { + ut := AllService.OauthService.UserThirdInfo(thirdType, uid) if ut.Id == 0 { return nil } @@ -187,12 +197,22 @@ func (us *UserService) InfoByGithubId(githubId string) *model.User { } // RegisterByGithub 注册 -func (us *UserService) RegisterByGithub(githubName string, githubId int64) *model.User { +func (us *UserService) RegisterByGithub(githubName string, githubId string) *model.User { + return us.RegisterByOauth(model.OauthTypeGithub, githubName, githubId) +} + +// RegisterByGoogle 注册 +func (us *UserService) RegisterByGoogle(name string, email string) *model.User { + return us.RegisterByOauth(model.OauthTypeGoogle, name, email) +} + +// RegisterByOauth 注册 +func (us *UserService) RegisterByOauth(thirdType, thirdName, uid string) *model.User { tx := global.DB.Begin() ut := &model.UserThird{ - OpenId: strconv.FormatInt(githubId, 10), - ThirdName: githubName, - ThirdType: model.OauthTypeGithub, + OpenId: uid, + ThirdName: thirdName, + ThirdType: thirdType, } //global.DB.Where("open_id = ?", githubId).First(ut) //这种情况不应该出现,如果出现说明有bug @@ -203,7 +223,7 @@ func (us *UserService) RegisterByGithub(githubName string, githubId int64) *mode // return u //} - username := us.GenerateUsernameByOauth(githubName) + username := us.GenerateUsernameByOauth(thirdName) u := &model.User{ Username: username, GroupId: 1,