前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文带你搞懂GitHub OAuth(下)

一文带你搞懂GitHub OAuth(下)

原创
作者头像
海风极客
发布2024-01-15 22:49:52
2670
发布2024-01-15 22:49:52
举报
文章被收录于专栏:Coding实践Coding实践
GitHub OAuth的基本流程

GitHub OAuth流程如下:

  1. 在GitHub上注册一个OAuth App。注册完成后,会获得Client ID和Client Secret。
  2. 编写代码,登录时请求GitHub API,跳转到用户授权页面。
  3. 用户同意授权后,GitHub会携带code参数,请求callback URL。
  4. 服务器接收请求后获得code,再携带code、client_id、client_secret等数据POST请求API。
  5. 从GitHub的响应中获得token。
  6. 携带token请求API,获得用户信息。

通过OAuth,第三方应用程序可以在用户授权的情况下安全地访问GitHub上的数据,而不需要获取用户的GitHub凭据。

下面两张图是基于以上流程所画出:

第一张图是极简版的,前后端协调,将利用Token获取UserInfo的流程挪到了前端实现,这样的话确实可以使用最少的代码实现功能,但我个人感觉不够安全。

第二张图的流程相对丰富,本篇文章的代码也是使用的该流程,区别上面那张图的地方主要是Token获取UserInfo的流程也放在后端,并且通过cookie的方式将身份给到前端,这样我们可以更好的灵活控制cookie的作用域、过期时间等,相比第一张图的流程复杂但安全了一些。

不到200行代码实现一个GitHub OAuth!

首先是Server端代码,会有三个接口:

  • /login:客户端点击登录时访问,也是系统的默认登录接口,访问时会重定向返回一个跳转地址。
  • /token:授权回调地址,主要是GitHub OAuth服务方授权时请求的地址,用于返回code。
  • /userInfo:根据cookie获取用户信息地址,cookie取自于HTTP请求的header中。

服务端代码:

代码语言:go
复制
var userMap = map[string]UserInfo{}

const (
	githubUri         = "https://github.com/login/oauth/authorize"
	githubAccessToken = "https://github.com/login/oauth/access_token"
	githubUserApi     = "https://api.github.com/user"
	redirectUri       = "http://localhost:8080/token" //地址必须注册到github的配置中
	clientID          = ""                            //TODO 填写自己的clientID
	clientSecret      = ""                            //TODO 填写自己的clientSecret
	sessionKey        = "test"
)

func main() {
	userMap = make(map[string]UserInfo)

	//前端静态文件地址
	fs := http.FileServer(http.Dir("public"))
	http.Handle("/", fs)

	//请求登录接口
	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		uri := fmt.Sprintf("%s?client_id=%s&redirect_uri=%s", githubUri, clientID, url.QueryEscape(redirectUri))
		http.Redirect(w, r, uri, http.StatusFound)
	})

	//重定向时根据code获取token接口
	http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
		httpClient := http.Client{}
		if err := r.ParseForm(); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		code := r.FormValue("code")

		reqURL := fmt.Sprintf("%s?client_id=%s&client_secret=%s&code=%s", githubAccessToken, clientID, clientSecret, code)
		req, err := http.NewRequest(http.MethodPost, reqURL, nil)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		req.Header.Set("accept", "application/json")
		res, err := httpClient.Do(req)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		defer func() {
			_ = res.Body.Close()
		}()

		var t OAuthAccessResponse
		if err = json.NewDecoder(res.Body).Decode(&t); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		//set cookie
		cookie, err := genCookie(t.AccessToken)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		http.SetCookie(w, &http.Cookie{Name: sessionKey, Value: cookie, Path: "/", Domain: "localhost", Expires: time.Now().Add(time.Second * 3600)})

		w.Header().Set("Location", "/welcome.html")
		w.WriteHeader(http.StatusFound)
	})

	http.HandleFunc("/userinfo", func(w http.ResponseWriter, r *http.Request) {
		cookie, err := r.Cookie(sessionKey)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		bytes, err := json.Marshal(userMap[cookie.Value])
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		_, _ = w.Write(bytes)
	})
	_ = http.ListenAndServe(":8080", nil)
}

//根据token获取userInfo,置换出自定义的cookie
func genCookie(token string) (string, error) {
	httpClient := http.Client{}
	req, err := http.NewRequest(http.MethodGet, githubUserApi, nil)
	if err != nil {
		return "", err
	}

	req.Header.Set("Authorization", "token "+token)
	res, err := httpClient.Do(req)
	if err != nil {
		return "", err
	}
	bytes, err := io.ReadAll(res.Body)
	if err != nil {
		return "", err
	}

	cookie := uuid.NewString()
	var userInfo UserInfo
	if err = json.Unmarshal(bytes, &userInfo); err != nil {
		return "", err
	}

	userMap[cookie] = userInfo
	return cookie, nil
}

type OAuthAccessResponse struct {
	AccessToken string `json:"access_token"`
}

Client端代码:

客户端代码就比较简单了,就是携带token请求后端的userInfo接口获取用户信息。

代码语言:html
复制
<body>
<a href="http://localhost:8080/login">点击登录</a>
</body>
<script>
    fetch('http://localhost:8080/userinfo', {}).then(res => res.json())
        .then(res => {
            const nameNode = document.createTextNode(`Welcome, ${res.name}`)
            document.body.appendChild(nameNode)
        })
</script>

注意点!!!

需要提前将完整的token接口的地址配置在GitHub OAuth App的配置中,否则会报错。

回顾总结

本文详细解读了GitHub OAuth的相关概念和操作流程。通过了解OAuth协议的工作原理和GitHub OAuth的应用场景,读者可以更好地理解如何通过OAuth授权机制安全地访问和使用GitHub上的资源。文章还提供了具体的操作步骤和注意事项,帮助读者在实际操作中避免常见问题。对于需要使用GitHub OAuth的开发者和用户来说,本文是一篇非常实用的参考资料。

参考:

https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps

https://blog.csdn.net/Mr_YanMingXin/article/details/126214065

我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • GitHub OAuth的基本流程
  • 不到200行代码实现一个GitHub OAuth!
  • 回顾总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档