ljw 1 год назад
Родитель
Сommit
1e3403e3c5
8 измененных файлов с 150 добавлено и 26 удалено
  1. 3 3
      README.md
  2. 2 2
      README_EN.md
  3. 1 1
      conf/config.yaml
  4. BIN
      docs/pc_login.png
  5. BIN
      docs/web_admin_oauth.png
  6. 68 11
      http/controller/api/ouath.go
  7. 50 3
      service/oauth.go
  8. 26 6
      service/user.go

+ 3 - 3
README.md

@@ -36,8 +36,8 @@
36 36
 
37 37
 #### 登录
38 38
 
39
-- 添加了`github`登录,需要在后台配置好就可以用了,具体可看后台OAuth配置
40
-- 添加了web后台授权登录
39
+- 添加了`github`和`google`授权登录,需要在后台配置好就可以用了,具体可看后台OAuth配置
40
+- 添加了web后台授权登录,点击后直接登录后台就自动登录客户端了
41 41
 
42 42
 ![pc_login](docs/pc_login.png)
43 43
 
@@ -66,7 +66,7 @@
66 66
    ![web_admin_gr](docs/web_admin_gr.png)
67 67
 5. 可以直接打开webclient,方便使用
68 68
    ![web_webclient](docs/admin_webclient.png)
69
-6. Oauth,暂时只支持了`Github`, 需要创建一个`OAuth App`,然后配置到后台
69
+6. Oauth,暂时只支持了`Github`和`Google`, 需要创建一个`OAuth App`,然后配置到后台
70 70
    ![web_admin_oauth](docs/web_admin_oauth.png)
71 71
     - `github oauth app`在`Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
72 72
       中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)

+ 2 - 2
README_EN.md

@@ -36,7 +36,7 @@ desktop software that provides self-hosted solutions.
36 36
 
37 37
 #### Login
38 38
 
39
-- Added `GitHub` login, which can be used after configuration in the admin panel. See the OAuth configuration section
39
+- Added `GitHub` and `Google` login, which can be used after configuration in the admin panel. See the OAuth configuration section
40 40
   for details.
41 41
 - Added authorization login for the web admin panel.
42 42
 
@@ -67,7 +67,7 @@ installation are `admin` `admin`, please change the password immediately.***
67 67
    ![web_admin_gr](docs/web_admin_gr.png)
68 68
 5. You can open the web client directly for convenience:
69 69
    ![web_webclient](docs/admin_webclient.png)
70
-6. OAuth support: Currently, only `GitHub` is supported. You need to create an `OAuth App` and configure it in the admin
70
+6. OAuth support: Currently, `GitHub` and `Google`  is supported. You need to create an `OAuth App` and configure it in the admin
71 71
    panel.
72 72
    ![web_admin_oauth](docs/web_admin_oauth.png)
73 73
     - Create a `GitHub OAuth App`

+ 1 - 1
conf/config.yaml

@@ -19,7 +19,7 @@ rustdesk:
19 19
   key: "123456789"
20 20
 logger:
21 21
   path: "./runtime/log.txt"
22
-  level: "error" #trace,debug,info,warn,error,fatal
22
+  level: "warn" #trace,debug,info,warn,error,fatal
23 23
   report-caller: true
24 24
 redis:
25 25
   addr: "127.0.0.1:6379"

BIN
docs/pc_login.png


BIN
docs/web_admin_oauth.png


+ 68 - 11
http/controller/api/ouath.go

@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/gin-gonic/gin"
11 11
 	"net/http"
12 12
 	"strconv"
13
+	"strings"
13 14
 )
14 15
 
15 16
 type Oauth struct {
@@ -161,9 +162,8 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
161 162
 			}
162 163
 			c.String(http.StatusOK, "绑定成功")
163 164
 			return
164
-		}
165
-		//登录
166
-		if ac == service.OauthActionTypeLogin {
165
+		} else if ac == service.OauthActionTypeLogin {
166
+			//登录
167 167
 			if v.UserId != 0 {
168 168
 				c.String(http.StatusInternalServerError, "授权已经成功")
169 169
 				return
@@ -181,7 +181,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
181 181
 				}
182 182
 
183 183
 				//自动注册
184
-				u = service.AllService.UserService.RegisterByGithub(userData.Login, int64(userData.Id))
184
+				u = service.AllService.UserService.RegisterByGithub(userData.Login, strconv.Itoa(userData.Id))
185 185
 				if u.Id == 0 {
186 186
 					c.String(http.StatusInternalServerError, "注册失败")
187 187
 					return
@@ -193,19 +193,76 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
193 193
 			c.String(http.StatusOK, "授权成功")
194 194
 			return
195 195
 		}
196
-
197 196
 		//返回js
198 197
 		c.Header("Content-Type", "text/html; charset=utf-8")
199 198
 		c.String(http.StatusOK, "授权错误")
200
-		//up := &apiResp.UserPayload{}
201
-		//c.JSON(http.StatusOK, apiResp.LoginRes{
202
-		//	AccessToken: ut.Token,
203
-		//	Type:        "access_token",
204
-		//	User:        *up.FromUser(u),
205
-		//})
206 199
 
207 200
 	}
208 201
 
202
+	if ty == model.OauthTypeGoogle {
203
+		code := c.Query("code")
204
+		err, userData := service.AllService.OauthService.GoogleCallback(code)
205
+		if err != nil {
206
+			c.String(http.StatusInternalServerError, "授权失败:"+err.Error())
207
+			return
208
+		}
209
+		//将空格替换成_
210
+		googleName := strings.Replace(userData.Name, " ", "_", -1)
211
+		if ac == service.OauthActionTypeBind {
212
+			//fmt.Println("bind", ty, userData)
213
+			utr := service.AllService.OauthService.UserThirdInfo(ty, userData.Email)
214
+			if utr.UserId > 0 {
215
+				c.String(http.StatusInternalServerError, "已经绑定其他账号")
216
+				return
217
+			}
218
+			//绑定
219
+			u := service.AllService.UserService.InfoById(v.UserId)
220
+			if u == nil {
221
+				c.String(http.StatusInternalServerError, "用户不存在")
222
+				return
223
+			}
224
+			//绑定
225
+			err = service.AllService.OauthService.BindGoogleUser(userData.Email, googleName, v.UserId)
226
+			if err != nil {
227
+				c.String(http.StatusInternalServerError, "绑定失败")
228
+				return
229
+			}
230
+			c.String(http.StatusOK, "绑定成功")
231
+			return
232
+		} else if ac == service.OauthActionTypeLogin {
233
+			if v.UserId != 0 {
234
+				c.String(http.StatusInternalServerError, "授权已经成功")
235
+				return
236
+			}
237
+			u := service.AllService.UserService.InfoByGoogleEmail(userData.Email)
238
+			if u == nil {
239
+				oa := service.AllService.OauthService.InfoByOp(ty)
240
+				if !*oa.AutoRegister {
241
+					//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
242
+
243
+					v.ThirdName = googleName
244
+					v.ThirdOpenId = userData.Email
245
+					url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
246
+					c.Redirect(http.StatusFound, url)
247
+					return
248
+				}
249
+
250
+				//自动注册
251
+				u = service.AllService.UserService.RegisterByGoogle(googleName, userData.Email)
252
+				if u.Id == 0 {
253
+					c.String(http.StatusInternalServerError, "注册失败")
254
+					return
255
+				}
256
+			}
257
+
258
+			v.UserId = u.Id
259
+			service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
260
+			c.String(http.StatusOK, "授权成功")
261
+			return
262
+		}
263
+	}
264
+	c.String(http.StatusInternalServerError, "授权配置错误,请联系管理员")
265
+
209 266
 }
210 267
 
211 268
 // WebOauthLogin

+ 50 - 3
service/oauth.go

@@ -68,7 +68,15 @@ type GithubUserdata struct {
68 68
 	UpdatedAt               time.Time `json:"updated_at"`
69 69
 	Url                     string    `json:"url"`
70 70
 }
71
-
71
+type GoogleUserdata struct {
72
+	Email         string `json:"email"`
73
+	FamilyName    string `json:"family_name"`
74
+	GivenName     string `json:"given_name"`
75
+	Id            string `json:"id"`
76
+	Name          string `json:"name"`
77
+	Picture       string `json:"picture"`
78
+	VerifiedEmail bool   `json:"verified_email"`
79
+}
72 80
 type OauthCacheItem struct {
73 81
 	UserId      uint   `json:"user_id"`
74 82
 	Id          string `json:"id"` //rustdesk的设备ID
@@ -187,7 +195,7 @@ func (os *OauthService) GithubCallback(code string) (error error, userData *Gith
187 195
 	}(resp.Body)
188 196
 
189 197
 	// 在这里处理 GitHub 用户信息
190
-	if err := json.NewDecoder(resp.Body).Decode(&userData); err != nil {
198
+	if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil {
191 199
 		global.Logger.Warn("failed decoding user info: %s\n", err)
192 200
 		error = errors.New("解析user info失败")
193 201
 		return
@@ -195,6 +203,37 @@ func (os *OauthService) GithubCallback(code string) (error error, userData *Gith
195 203
 	return
196 204
 }
197 205
 
206
+func (os *OauthService) GoogleCallback(code string) (error error, userData *GoogleUserdata) {
207
+	err, oauthConfig := os.GetOauthConfig(model.OauthTypeGoogle)
208
+	token, err := oauthConfig.Exchange(context.Background(), code)
209
+	if err != nil {
210
+		global.Logger.Warn(fmt.Printf("oauthConfig.Exchange() failed: %s\n", err))
211
+		error = errors.New("获取token失败")
212
+		return
213
+	}
214
+	// 创建 HTTP 客户端,并将 access_token 添加到 Authorization 头中
215
+	client := oauthConfig.Client(context.Background(), token)
216
+	resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo")
217
+	if err != nil {
218
+		global.Logger.Warn("failed getting user info: %s\n", err)
219
+		error = errors.New("获取user info失败: " + err.Error())
220
+		return
221
+	}
222
+	defer func(Body io.ReadCloser) {
223
+		err := Body.Close()
224
+		if err != nil {
225
+			global.Logger.Warn("failed closing response body: %s\n", err)
226
+		}
227
+	}(resp.Body)
228
+
229
+	if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil {
230
+		global.Logger.Warn("failed decoding user info: %s\n", err)
231
+		error = errors.New("解析user info失败:" + err.Error())
232
+		return
233
+	}
234
+	return
235
+}
236
+
198 237
 func (os *OauthService) UserThirdInfo(op, openid string) *model.UserThird {
199 238
 	ut := &model.UserThird{}
200 239
 	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 {
202 241
 }
203 242
 
204 243
 func (os *OauthService) BindGithubUser(openid, username string, userId uint) error {
244
+	return os.BindOauthUser(model.OauthTypeGithub, openid, username, userId)
245
+}
246
+
247
+func (os *OauthService) BindGoogleUser(email, username string, userId uint) error {
248
+	return os.BindOauthUser(model.OauthTypeGoogle, email, username, userId)
249
+}
250
+func (os *OauthService) BindOauthUser(thirdType, openid, username string, userId uint) error {
205 251
 	utr := &model.UserThird{
206 252
 		OpenId:    openid,
207
-		ThirdType: model.OauthTypeGithub,
253
+		ThirdType: thirdType,
208 254
 		ThirdName: username,
209 255
 		UserId:    userId,
210 256
 	}
211 257
 	return global.DB.Create(utr).Error
212 258
 }
259
+
213 260
 func (os *OauthService) UnBindGithubUser(userid uint) error {
214 261
 	return global.DB.Where("user_id = ? and third_type = ?", userid, model.OauthTypeGithub).Delete(&model.UserThird{}).Error
215 262
 }

+ 26 - 6
service/user.go

@@ -175,7 +175,17 @@ func (us *UserService) RouteNames(u *model.User) []string {
175 175
 
176 176
 // InfoByGithubId 根据githubid取用户信息
177 177
 func (us *UserService) InfoByGithubId(githubId string) *model.User {
178
-	ut := AllService.OauthService.UserThirdInfo(model.OauthTypeGithub, githubId)
178
+	return us.InfoByOauthId(model.OauthTypeGithub, githubId)
179
+}
180
+
181
+// InfoByGoogleEmail 根据googleid取用户信息
182
+func (us *UserService) InfoByGoogleEmail(email string) *model.User {
183
+	return us.InfoByOauthId(model.OauthTypeGithub, email)
184
+}
185
+
186
+// InfoByOauthId 根据oauth取用户信息
187
+func (us *UserService) InfoByOauthId(thirdType, uid string) *model.User {
188
+	ut := AllService.OauthService.UserThirdInfo(thirdType, uid)
179 189
 	if ut.Id == 0 {
180 190
 		return nil
181 191
 	}
@@ -187,12 +197,22 @@ func (us *UserService) InfoByGithubId(githubId string) *model.User {
187 197
 }
188 198
 
189 199
 // RegisterByGithub 注册
190
-func (us *UserService) RegisterByGithub(githubName string, githubId int64) *model.User {
200
+func (us *UserService) RegisterByGithub(githubName string, githubId string) *model.User {
201
+	return us.RegisterByOauth(model.OauthTypeGithub, githubName, githubId)
202
+}
203
+
204
+// RegisterByGoogle 注册
205
+func (us *UserService) RegisterByGoogle(name string, email string) *model.User {
206
+	return us.RegisterByOauth(model.OauthTypeGoogle, name, email)
207
+}
208
+
209
+// RegisterByOauth 注册
210
+func (us *UserService) RegisterByOauth(thirdType, thirdName, uid string) *model.User {
191 211
 	tx := global.DB.Begin()
192 212
 	ut := &model.UserThird{
193
-		OpenId:    strconv.FormatInt(githubId, 10),
194
-		ThirdName: githubName,
195
-		ThirdType: model.OauthTypeGithub,
213
+		OpenId:    uid,
214
+		ThirdName: thirdName,
215
+		ThirdType: thirdType,
196 216
 	}
197 217
 	//global.DB.Where("open_id = ?", githubId).First(ut)
198 218
 	//这种情况不应该出现,如果出现说明有bug
@@ -203,7 +223,7 @@ func (us *UserService) RegisterByGithub(githubName string, githubId int64) *mode
203 223
 	//	return u
204 224
 	//}
205 225
 
206
-	username := us.GenerateUsernameByOauth(githubName)
226
+	username := us.GenerateUsernameByOauth(thirdName)
207 227
 	u := &model.User{
208 228
 		Username: username,
209 229
 		GroupId:  1,