第三方登录 GitHub OAuth的Golang实现

968 阅读2分钟

Github官方文档

Gin 示例代码

本篇展示了使用golang的gin框架来实现github第三方登录的一个过程。

创建OAuth应用程序

首先按照网上的教程在github的settings/Developer settings中新建一个OAuth Apps。填写信息如下:

截屏2023-01-10 11.20.22.png

需要注意的是,callback URL必须和你开发的程序的回调程序相同,否则无法成功回调。创建完OAuth Apps可以获得第三方登录所需要的client_id以及client_secret供下一步使用。

基于Golang Gin框架的Demo 展示

这里按照github官方文档所提供的步骤一步一步的获得github三方登录的信息。

第一步请求获得github身份信息,附加参数有client_id,redirect_uri和state。state的作用用于防止跨站请求伪造攻击。在成功请求后会github会对上一步的回调url进行请求,同时在query中返回code参数。

截屏2023-01-10 11.27.18.png

func HandlerGithubLogin(c *gin.Context) {
	// GET https://github.com/login/oauth/authorize
	conf := lib.LoadServerConfig()
	// state string:An unguessable random string.
	// It is used to protect against cross-site request forgery attacks.
	state := "xxxxxxx"
	url := "https://github.com/login/oauth/authorize?client_id=" + conf.AppId + "&redirect_uri=" + conf.RedirectURI + "&state=" + state
	c.Redirect(http.StatusMovedPermanently, url)
}

在获得code参数后第二步就是根据code去申请access_token,然后再去获取用户信息。这里是采用POST的方法去请求,body的参数具体见下图以及示例代码。与第一步不同的是POST的body参数还需要client_secret和第一步获得的code。

截屏2023-01-10 11.35.16.png

截屏2023-01-10 11.36.23.png

// get access_token
func GetGithubToken(c *gin.Context) {
	// POST https://github.com/login/oauth/access_token
	conf := lib.LoadServerConfig()
	code := c.Query("code")
	loginUrl := "https://github.com/login/oauth/access_token?client_id=" + conf.AppId + "&client_secret=" + conf.AppKey + "&redirect_uri=" + conf.RedirectURI + "&code=" + code
	response, err := http.PostForm(loginUrl, url.Values{
		"client_id":     {conf.AppId},
		"client_secret": {conf.AppKey},
		"redirect_uri":  {conf.RedirectURI},
		"code":          {code},
	})
	if err != nil {
		fmt.Println("post error!", err.Error())
		return
	}
	defer response.Body.Close()
	bs, _ := ioutil.ReadAll(response.Body)
	body := string(bs)
	resultMap := lib.ConvertToMap(body)
	accessToken := resultMap["access_token"]
	GetGithubUserMessage(accessToken, c)
}

在获得access_token后,最后一步就是获得用户信息。token参数是附带在head中进行get请求的。

截屏2023-01-10 11.40.07.png

// get user data
func GetGithubUserMessage(access_token string, c *gin.Context) {
	// 	Authorization: Bearer OAUTH-TOKEN
	// GET https://api.github.com/user
	client := &http.Client{}
	reqest, err := http.NewRequest("GET", "https://api.github.com/user", nil)
	if err != nil {
		panic(err)
	}
	reqest.Header.Add("Authorization", "token "+access_token)
	resp, _ := client.Do(reqest)
	if err != nil {
		fmt.Println("GetMessage Err", err.Error())
		return
	}
	defer resp.Body.Close()
	// use for your website
	message, _ := lib.ParseResponse(resp)
	c.JSON(http.StatusOK,message)
}

最后用户结果示例如下:

{
	"login": "TomaChen513",
	"id": 000000,
	"node_id": "AAAAAAAAAA",
	"avatar_url": "https://avatars.githubusercontent.com",
	"gravatar_id": "",
	"url": "https://api.github.com/users",
	"html_url": "https://github.com",
	"followers_url": "https://api.github.com/users",
	"following_url": "https://api.github.com/users",
	"gists_url": "https://api.github.com/users",
	"starred_url": "https://api.github.com/users",
	"subscriptions_url": "https://api.github.com/users",
	"type": "User",
	"site_admin": false,
	"name": null,
	"company": null,
	"blog": "",
	"location": null,
	"email": null,
	"hireable": null,
	"bio": null,
	"twitter_username": null,
	"public_repos": 3,
	"public_gists": 0,
	"followers": 1000000,
	"following": 1000000,
	"created_at": "2019",
	"updated_at": "2023"
}